From 3adb1969a53fb22066453c36fe9d0acb6902d343 Mon Sep 17 00:00:00 2001 From: Pierre Date: Mon, 6 Jul 2020 13:20:28 +0200 Subject: [PATCH] SONAR-13590 fix branch+snapshot comparator --- .../issue/index/AsyncIssueIndexingImpl.java | 5 +- .../index/AsyncIssueIndexingImplTest.java | 41 ++++++++++ .../sonar/server/issue/index/Comparators.java | 74 +++++++++++++++++++ 3 files changed, 119 insertions(+), 1 deletion(-) create mode 100644 server/sonar-webserver-core/src/test/java/org/sonar/server/issue/index/Comparators.java diff --git a/server/sonar-webserver-core/src/main/java/org/sonar/server/issue/index/AsyncIssueIndexingImpl.java b/server/sonar-webserver-core/src/main/java/org/sonar/server/issue/index/AsyncIssueIndexingImpl.java index 8fd541c78f3..46aeff7537f 100644 --- a/server/sonar-webserver-core/src/main/java/org/sonar/server/issue/index/AsyncIssueIndexingImpl.java +++ b/server/sonar-webserver-core/src/main/java/org/sonar/server/issue/index/AsyncIssueIndexingImpl.java @@ -104,10 +104,13 @@ public class AsyncIssueIndexingImpl implements AsyncIssueIndexing { projectUuids.sort(compareBySnapshot(snapshotByProjectUuid)); } - private static Comparator compareBySnapshot(Map snapshotByProjectUuid) { + static Comparator compareBySnapshot(Map snapshotByProjectUuid) { return (uuid1, uuid2) -> { SnapshotDto snapshot1 = snapshotByProjectUuid.get(uuid1); SnapshotDto snapshot2 = snapshotByProjectUuid.get(uuid2); + if (snapshot1 == null && snapshot2 == null) { + return 0; + } if (snapshot1 == null) { return 1; } diff --git a/server/sonar-webserver-core/src/test/java/org/sonar/server/issue/index/AsyncIssueIndexingImplTest.java b/server/sonar-webserver-core/src/test/java/org/sonar/server/issue/index/AsyncIssueIndexingImplTest.java index c8c25d23fbe..7b00191049e 100644 --- a/server/sonar-webserver-core/src/test/java/org/sonar/server/issue/index/AsyncIssueIndexingImplTest.java +++ b/server/sonar-webserver-core/src/test/java/org/sonar/server/issue/index/AsyncIssueIndexingImplTest.java @@ -19,10 +19,13 @@ */ package org.sonar.server.issue.index; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Optional; import org.junit.Before; import org.junit.Rule; @@ -45,6 +48,7 @@ import org.sonar.db.component.BranchDto; import org.sonar.db.component.SnapshotDto; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatCode; import static org.assertj.core.api.Assertions.tuple; import static org.mockito.ArgumentMatchers.anyCollection; import static org.mockito.Mockito.mock; @@ -221,6 +225,43 @@ public class AsyncIssueIndexingImplTest { tuple("PULL_REQUEST", null, "pr_1")); } + @Test + public void verify_comparator_transitivity() { + Map map = new HashMap<>(); + map.put("A", new SnapshotDto().setCreatedAt(1L)); + map.put("B", new SnapshotDto().setCreatedAt(2L)); + map.put("C", new SnapshotDto().setCreatedAt(-1L)); + List uuids = new ArrayList<>(map.keySet()); + uuids.add("D"); + Comparators.verifyTransitivity(AsyncIssueIndexingImpl.compareBySnapshot(map), uuids); + } + + @Test + public void trigger_with_lot_of_not_analyzed_project_should_not_raise_exception() { + for (int i = 0; i < 100; i++) { + BranchDto dto = new BranchDto() + .setBranchType(BRANCH) + .setKey("branch_" + i) + .setUuid("branch_uuid" + i) + .setProjectUuid("project_uuid" + i); + dbClient.branchDao().insert(dbTester.getSession(), dto); + dbTester.commit(); + insertSnapshot("analysis_" + i, "project_uuid" + i, 1); + } + + for (int i = 100; i < 200; i++) { + BranchDto dto = new BranchDto() + .setBranchType(BRANCH) + .setKey("branch_" + i) + .setUuid("branch_uuid" + i) + .setProjectUuid("project_uuid" + i); + dbClient.branchDao().insert(dbTester.getSession(), dto); + dbTester.commit(); + } + + assertThatCode(underTest::triggerOnIndexCreation).doesNotThrowAnyException(); + } + private SnapshotDto insertSnapshot(String analysisUuid, String projectUuid, long createdAt) { SnapshotDto snapshot = new SnapshotDto() .setUuid(analysisUuid) diff --git a/server/sonar-webserver-core/src/test/java/org/sonar/server/issue/index/Comparators.java b/server/sonar-webserver-core/src/test/java/org/sonar/server/issue/index/Comparators.java new file mode 100644 index 00000000000..0d71657cc00 --- /dev/null +++ b/server/sonar-webserver-core/src/test/java/org/sonar/server/issue/index/Comparators.java @@ -0,0 +1,74 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.server.issue.index; + +import java.util.Collection; +import java.util.Comparator; + +public final class Comparators { + + /** + * Prevent construction. + */ + private Comparators() { + } + + /** + * Verify that a comparator is transitive. + * + * @param the type being compared + * @param comparator the comparator to test + * @param elements the elements to test against + * @throws AssertionError if the comparator is not transitive + */ + public static void verifyTransitivity(Comparator comparator, Collection elements) { + for (T first : elements) { + for (T second : elements) { + int result1 = comparator.compare(first, second); + int result2 = comparator.compare(second, first); + if (result1 != -result2) { + // Uncomment the following line to step through the failed case + // comparator.compare(first, second); + throw new AssertionError("compare(" + first + ", " + second + ") == " + result1 + + " but swapping the parameters returns " + result2); + } + } + } + for (T first : elements) { + for (T second : elements) { + int firstGreaterThanSecond = comparator.compare(first, second); + if (firstGreaterThanSecond <= 0) + continue; + for (T third : elements) { + int secondGreaterThanThird = comparator.compare(second, third); + if (secondGreaterThanThird <= 0) + continue; + int firstGreaterThanThird = comparator.compare(first, third); + if (firstGreaterThanThird <= 0) { + throw new AssertionError("compare(" + first + ", " + second + ") > 0, " + + "compare(" + second + ", " + third + ") > 0, but compare(" + first + ", " + third + ") == " + + firstGreaterThanThird); + } + } + } + } + } + +} -- 2.39.5