3 * Copyright (C) 2009-2022 SonarSource SA
4 * mailto:info AT sonarsource DOT com
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 3 of the License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 package org.sonar.server.duplication.ws;
22 import com.google.common.collect.Iterables;
23 import java.util.List;
24 import javax.annotation.Nullable;
25 import org.apache.commons.lang.RandomStringUtils;
26 import org.junit.Rule;
27 import org.junit.Test;
28 import org.sonar.db.DbTester;
29 import org.sonar.db.component.BranchType;
30 import org.sonar.db.component.ComponentDto;
31 import org.sonar.server.component.ComponentFinder;
33 import static java.lang.String.format;
34 import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
35 import static org.assertj.core.api.Assertions.assertThat;
36 import static org.sonar.db.component.ComponentTesting.newFileDto;
38 public class DuplicationsParserTest {
41 public DbTester db = DbTester.create();
43 private final DuplicationsParser parser = new DuplicationsParser(new ComponentFinder(db.getDbClient(), null));
46 public void empty_list_when_no_data() {
47 ComponentDto project = db.components().insertPrivateProject();
48 ComponentDto file = db.components().insertComponent(newFileDto(project));
50 assertThat(parser.parse(db.getSession(), file, null, null, null)).isEmpty();
54 public void duplication_on_same_file() {
55 ComponentDto project = db.components().insertPrivateProject();
56 ComponentDto file = db.components().insertComponent(newFileDto(project));
57 List<DuplicationsParser.Block> blocks = parser.parse(db.getSession(), file, null, null,
58 format("<duplications>\n" +
60 " <b s=\"31\" l=\"5\" r=\"%s\"/>\n" +
61 " <b s=\"20\" l=\"5\" r=\"%s\"/>\n" +
63 "</duplications>", file.getKey(), file.getKey()));
64 assertThat(blocks).hasSize(1);
66 List<Duplication> duplications = blocks.get(0).getDuplications();
67 assertThat(duplications).hasSize(2);
69 // Smallest line comes first
70 Duplication duplication1 = duplications.get(0);
71 assertThat(duplication1.componentDto()).isEqualTo(file);
72 assertThat(duplication1.from()).isEqualTo(20);
73 assertThat(duplication1.size()).isEqualTo(5);
75 Duplication duplication2 = duplications.get(1);
76 assertThat(duplication2.componentDto()).isEqualTo(file);
77 assertThat(duplication2.from()).isEqualTo(31);
78 assertThat(duplication2.size()).isEqualTo(5);
82 public void duplication_on_same_project() {
83 ComponentDto project = db.components().insertPrivateProject();
84 ComponentDto file1 = db.components().insertComponent(newFileDto(project));
85 ComponentDto file2 = db.components().insertComponent(newFileDto(project));
86 List<DuplicationsParser.Block> blocks = parser.parse(db.getSession(), file1, null, null,
87 format("<duplications>\n" +
89 " <b s=\"20\" l=\"5\" r=\"%s\"/>\n" +
90 " <b s=\"31\" l=\"5\" r=\"%s\"/>\n" +
92 "</duplications>", file2.getKey(), file1.getKey()));
93 assertThat(blocks).hasSize(1);
95 List<Duplication> duplications = blocks.get(0).getDuplications();
96 assertThat(duplications).hasSize(2);
98 // Current file comes first
99 Duplication duplication1 = duplications.get(0);
100 assertThat(duplication1.componentDto()).isEqualTo(file1);
101 assertThat(duplication1.from()).isEqualTo(31);
102 assertThat(duplication1.size()).isEqualTo(5);
104 Duplication duplication2 = duplications.get(1);
105 assertThat(duplication2.componentDto()).isEqualTo(file2);
106 assertThat(duplication2.from()).isEqualTo(20);
107 assertThat(duplication2.size()).isEqualTo(5);
111 public void duplications_on_different_project() {
112 ComponentDto project1 = db.components().insertPrivateProject();
113 ComponentDto file1 = db.components().insertComponent(newFileDto(project1));
114 ComponentDto file2 = db.components().insertComponent(newFileDto(project1));
115 ComponentDto project2 = db.components().insertPrivateProject();
116 ComponentDto fileOnProject2 = db.components().insertComponent(newFileDto(project2));
117 List<DuplicationsParser.Block> blocks = parser.parse(db.getSession(), file1, null, null,
118 format("<duplications>\n" +
120 " <b s=\"148\" l=\"24\" r=\"%s\"/>\n" +
121 " <b s=\"137\" l=\"24\" r=\"%s\"/>\n" +
122 " <b s=\"111\" l=\"24\" r=\"%s\"/>\n" +
124 "</duplications>", file1.getKey(), fileOnProject2.getKey(), file2.getKey()));
125 assertThat(blocks).hasSize(1);
127 List<Duplication> duplications = blocks.get(0).getDuplications();
128 assertThat(duplications).hasSize(3);
130 // Current file's project comes first
132 Duplication duplication1 = duplications.get(0);
133 assertThat(duplication1.componentDto()).isEqualTo(file1);
134 assertThat(duplication1.from()).isEqualTo(148);
135 assertThat(duplication1.size()).isEqualTo(24);
137 Duplication duplication2 = duplications.get(1);
138 assertThat(duplication2.componentDto()).isEqualTo(file2);
139 assertThat(duplication2.from()).isEqualTo(111);
140 assertThat(duplication2.size()).isEqualTo(24);
142 // Other project comes last
144 Duplication duplication3 = duplications.get(2);
145 assertThat(duplication3.componentDto()).isEqualTo(fileOnProject2);
146 assertThat(duplication3.from()).isEqualTo(137);
147 assertThat(duplication3.size()).isEqualTo(24);
151 public void duplications_on_many_blocks() {
152 ComponentDto project1 = db.components().insertPrivateProject();
153 ComponentDto file1 = db.components().insertComponent(newFileDto(project1)
154 .setKey("org.codehaus.sonar:sonar-plugin-api:src/main/java/org/sonar/api/utils/command/CommandExecutor.java")
155 .setLongName("CommandExecutor"));
156 ComponentDto project2 = db.components().insertPrivateProject();
157 ComponentDto file2 = db.components().insertComponent(newFileDto(project2)
158 .setKey("com.sonarsource.orchestrator:sonar-orchestrator:src/main/java/com/sonar/orchestrator/util/CommandExecutor.java")
159 .setLongName("CommandExecutor"));
160 List<DuplicationsParser.Block> blocks = parser.parse(db.getSession(), file1, null, null,
161 format("<duplications>\n" +
163 " <b s=\"94\" l=\"101\" r=\"%s\"/>\n" +
164 " <b s=\"83\" l=\"101\" r=\"%s\"/>\n" +
167 " <b s=\"38\" l=\"40\" r=\"%s\"/>\n" +
168 " <b s=\"29\" l=\"39\" r=\"%s\"/>\n" +
170 "</duplications>\n", file2.getKey(), file1.getKey(), file2.getKey(), file1.getKey()));
171 assertThat(blocks).hasSize(2);
173 // Block with smaller line should come first
175 assertThat(blocks.get(0).getDuplications().get(0).from()).isEqualTo(29);
176 assertThat(blocks.get(0).getDuplications().get(1).from()).isEqualTo(38);
178 assertThat(blocks.get(1).getDuplications().get(0).from()).isEqualTo(83);
179 assertThat(blocks.get(1).getDuplications().get(1).from()).isEqualTo(94);
183 public void duplication_on_not_existing_file() {
184 ComponentDto project = db.components().insertPrivateProject();
185 ComponentDto file = db.components().insertComponent(newFileDto(project));
186 List<DuplicationsParser.Block> blocks = parser.parse(db.getSession(), file, null, null,
187 format("<duplications>\n" +
189 " <b s=\"20\" l=\"5\" r=\"%s\"/>\n" +
190 " <b s=\"31\" l=\"5\" r=\"%s\"/>\n" +
192 "</duplications>", file.getKey(), "not_existing"));
193 assertThat(blocks).hasSize(1);
195 List<Duplication> duplications = blocks.get(0).getDuplications();
196 assertThat(duplications).hasSize(2);
198 // Duplications on removed file
199 Duplication duplication1 = duplication(duplications, null);
200 assertThat(duplication1.componentDto()).isNull();
201 assertThat(duplication1.from()).isEqualTo(31);
202 assertThat(duplication1.size()).isEqualTo(5);
204 Duplication duplication2 = duplication(duplications, file.getKey());
205 assertThat(duplication2.componentDto()).isEqualTo(file);
206 assertThat(duplication2.from()).isEqualTo(20);
207 assertThat(duplication2.size()).isEqualTo(5);
211 public void compare_duplications() {
212 ComponentDto project1 = db.components().insertPrivateProject();
213 ComponentDto project2 = db.components().insertPrivateProject();
214 ComponentDto currentFile = db.components().insertComponent(newFileDto(project1, null));
215 ComponentDto fileOnSameProject = db.components().insertComponent(newFileDto(project1, null));
216 ComponentDto fileOnDifferentProject = db.components().insertComponent(newFileDto(project2, null));
218 DuplicationsParser.DuplicationComparator comparator = new DuplicationsParser.DuplicationComparator(currentFile.uuid(), currentFile.branchUuid());
221 assertThat(comparator.compare(Duplication.newComponent(currentFile, 2, 2),
222 Duplication.newComponent(currentFile, 5, 2))).isEqualTo(-1);
223 // Different files on same project
224 assertThat(comparator.compare(Duplication.newComponent(currentFile, 2, 2),
225 Duplication.newComponent(fileOnSameProject, 5, 2))).isEqualTo(-1);
226 assertThat(comparator.compare(Duplication.newComponent(fileOnSameProject, 2, 2),
227 Duplication.newComponent(currentFile, 5, 2))).isOne();
228 // Different files on different projects
229 assertThat(comparator.compare(Duplication.newComponent(fileOnSameProject, 5, 2),
230 Duplication.newComponent(fileOnDifferentProject, 2, 2))).isEqualTo(-1);
231 assertThat(comparator.compare(Duplication.newComponent(fileOnDifferentProject, 5, 2),
232 Duplication.newComponent(fileOnSameProject, 2, 2))).isOne();
233 // Files on 2 different projects
234 ComponentDto project3 = db.components().insertPrivateProject();
235 assertThat(comparator.compare(Duplication.newComponent(fileOnDifferentProject, 5, 2),
236 Duplication.newComponent(project3, 2, 2))).isOne();
238 // With null duplications
239 assertThat(comparator.compare(null, Duplication.newComponent(fileOnSameProject, 2, 2))).isEqualTo(-1);
240 assertThat(comparator.compare(Duplication.newComponent(fileOnSameProject, 2, 2), null)).isEqualTo(-1);
241 assertThat(comparator.compare(null, null)).isEqualTo(-1);
243 // On some removed file
244 assertThat(comparator.compare(Duplication.newComponent(currentFile, 2, 2),
245 Duplication.newRemovedComponent("key1", 5, 2))).isEqualTo(-1);
246 assertThat(comparator.compare(Duplication.newRemovedComponent("key2", 2, 2),
247 Duplication.newComponent(currentFile, 5, 2))).isOne();
251 public void duplication_on_branch() {
252 ComponentDto project = db.components().insertPublicProject();
253 String branchName = randomAlphanumeric(248);
254 ComponentDto branch = db.components().insertProjectBranch(project, b -> b.setKey(branchName));
255 ComponentDto file1 = db.components().insertComponent(newFileDto(branch));
256 ComponentDto file2 = db.components().insertComponent(newFileDto(branch));
257 List<DuplicationsParser.Block> blocks = parser.parse(db.getSession(), file1, branchName, null,
258 format("<duplications>\n" +
260 " <b s=\"20\" l=\"5\" r=\"%s\"/>\n" +
261 " <b s=\"31\" l=\"5\" r=\"%s\"/>\n" +
263 "</duplications>", file2.getKey(), file1.getKey()));
264 assertThat(blocks).hasSize(1);
266 List<Duplication> duplications = blocks.get(0).getDuplications();
267 assertThat(duplications).hasSize(2);
269 // Current file comes first
270 Duplication duplication1 = duplications.get(0);
271 assertThat(duplication1.componentDto()).isEqualTo(file1);
272 assertThat(duplication1.componentDto().getKey()).isEqualTo(file1.getKey());
273 assertThat(duplication1.from()).isEqualTo(31);
274 assertThat(duplication1.size()).isEqualTo(5);
276 Duplication duplication2 = duplications.get(1);
277 assertThat(duplication2.componentDto()).isEqualTo(file2);
278 assertThat(duplication2.componentDto().getKey()).isEqualTo(file2.getKey());
279 assertThat(duplication2.from()).isEqualTo(20);
280 assertThat(duplication2.size()).isEqualTo(5);
284 public void duplication_on_pull_request() {
285 ComponentDto project = db.components().insertPublicProject();
286 String pullRequestKey = RandomStringUtils.randomAlphanumeric(100);
287 ComponentDto pullRequest = db.components().insertProjectBranch(project, b -> b.setBranchType(BranchType.PULL_REQUEST).setKey(pullRequestKey));
288 ComponentDto file1 = db.components().insertComponent(newFileDto(pullRequest));
289 ComponentDto file2 = db.components().insertComponent(newFileDto(pullRequest));
290 List<DuplicationsParser.Block> blocks = parser.parse(db.getSession(), file1, null, pullRequestKey,
291 format("<duplications>\n" +
293 " <b s=\"20\" l=\"5\" r=\"%s\"/>\n" +
294 " <b s=\"31\" l=\"5\" r=\"%s\"/>\n" +
296 "</duplications>", file2.getKey(), file1.getKey()));
297 assertThat(blocks).hasSize(1);
299 List<Duplication> duplications = blocks.get(0).getDuplications();
300 assertThat(duplications).hasSize(2);
302 // Current file comes first
303 Duplication duplication1 = duplications.get(0);
304 assertThat(duplication1.componentDto()).isEqualTo(file1);
305 assertThat(duplication1.componentDto().getKey()).isEqualTo(file1.getKey());
306 assertThat(duplication1.from()).isEqualTo(31);
307 assertThat(duplication1.size()).isEqualTo(5);
309 Duplication duplication2 = duplications.get(1);
310 assertThat(duplication2.componentDto()).isEqualTo(file2);
311 assertThat(duplication2.componentDto().getKey()).isEqualTo(file2.getKey());
312 assertThat(duplication2.from()).isEqualTo(20);
313 assertThat(duplication2.size()).isEqualTo(5);
316 private static Duplication duplication(List<Duplication> duplications, @Nullable final String componentKey) {
317 return Iterables.find(duplications, input -> input != null && (componentKey == null ? input.componentDto() == null
318 : input.componentDto() != null && componentKey.equals(input.componentDto().getKey())));