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;
32 import static java.lang.String.format;
33 import static org.assertj.core.api.Assertions.assertThat;
34 import static org.sonar.db.component.ComponentTesting.newFileDto;
36 public class DuplicationsParserTest {
39 public DbTester db = DbTester.create();
41 private DuplicationsParser parser = new DuplicationsParser(db.getDbClient().componentDao());
44 public void empty_list_when_no_data() {
45 ComponentDto project = db.components().insertPrivateProject();
46 ComponentDto file = db.components().insertComponent(newFileDto(project));
48 assertThat(parser.parse(db.getSession(), file, null, null, null)).isEmpty();
52 public void duplication_on_same_file() {
53 ComponentDto project = db.components().insertPrivateProject();
54 ComponentDto file = db.components().insertComponent(newFileDto(project));
55 List<DuplicationsParser.Block> blocks = parser.parse(db.getSession(), file, null, null,
56 format("<duplications>\n" +
58 " <b s=\"31\" l=\"5\" r=\"%s\"/>\n" +
59 " <b s=\"20\" l=\"5\" r=\"%s\"/>\n" +
61 "</duplications>", file.getKey(), file.getKey()));
62 assertThat(blocks).hasSize(1);
64 List<Duplication> duplications = blocks.get(0).getDuplications();
65 assertThat(duplications).hasSize(2);
67 // Smallest line comes first
68 Duplication duplication1 = duplications.get(0);
69 assertThat(duplication1.componentDto()).isEqualTo(file);
70 assertThat(duplication1.from()).isEqualTo(20);
71 assertThat(duplication1.size()).isEqualTo(5);
73 Duplication duplication2 = duplications.get(1);
74 assertThat(duplication2.componentDto()).isEqualTo(file);
75 assertThat(duplication2.from()).isEqualTo(31);
76 assertThat(duplication2.size()).isEqualTo(5);
80 public void duplication_on_same_project() {
81 ComponentDto project = db.components().insertPrivateProject();
82 ComponentDto file1 = db.components().insertComponent(newFileDto(project));
83 ComponentDto file2 = db.components().insertComponent(newFileDto(project));
84 List<DuplicationsParser.Block> blocks = parser.parse(db.getSession(), file1, null, null,
85 format("<duplications>\n" +
87 " <b s=\"20\" l=\"5\" r=\"%s\"/>\n" +
88 " <b s=\"31\" l=\"5\" r=\"%s\"/>\n" +
90 "</duplications>", file2.getKey(), file1.getKey()));
91 assertThat(blocks).hasSize(1);
93 List<Duplication> duplications = blocks.get(0).getDuplications();
94 assertThat(duplications).hasSize(2);
96 // Current file comes first
97 Duplication duplication1 = duplications.get(0);
98 assertThat(duplication1.componentDto()).isEqualTo(file1);
99 assertThat(duplication1.from()).isEqualTo(31);
100 assertThat(duplication1.size()).isEqualTo(5);
102 Duplication duplication2 = duplications.get(1);
103 assertThat(duplication2.componentDto()).isEqualTo(file2);
104 assertThat(duplication2.from()).isEqualTo(20);
105 assertThat(duplication2.size()).isEqualTo(5);
109 public void duplications_on_different_project() {
110 ComponentDto project1 = db.components().insertPrivateProject();
111 ComponentDto file1 = db.components().insertComponent(newFileDto(project1));
112 ComponentDto file2 = db.components().insertComponent(newFileDto(project1));
113 ComponentDto project2 = db.components().insertPrivateProject();
114 ComponentDto fileOnProject2 = db.components().insertComponent(newFileDto(project2));
115 List<DuplicationsParser.Block> blocks = parser.parse(db.getSession(), file1, null, null,
116 format("<duplications>\n" +
118 " <b s=\"148\" l=\"24\" r=\"%s\"/>\n" +
119 " <b s=\"137\" l=\"24\" r=\"%s\"/>\n" +
120 " <b s=\"111\" l=\"24\" r=\"%s\"/>\n" +
122 "</duplications>", file1.getKey(), fileOnProject2.getKey(), file2.getKey()));
123 assertThat(blocks).hasSize(1);
125 List<Duplication> duplications = blocks.get(0).getDuplications();
126 assertThat(duplications).hasSize(3);
128 // Current file's project comes first
130 Duplication duplication1 = duplications.get(0);
131 assertThat(duplication1.componentDto()).isEqualTo(file1);
132 assertThat(duplication1.from()).isEqualTo(148);
133 assertThat(duplication1.size()).isEqualTo(24);
135 Duplication duplication2 = duplications.get(1);
136 assertThat(duplication2.componentDto()).isEqualTo(file2);
137 assertThat(duplication2.from()).isEqualTo(111);
138 assertThat(duplication2.size()).isEqualTo(24);
140 // Other project comes last
142 Duplication duplication3 = duplications.get(2);
143 assertThat(duplication3.componentDto()).isEqualTo(fileOnProject2);
144 assertThat(duplication3.from()).isEqualTo(137);
145 assertThat(duplication3.size()).isEqualTo(24);
149 public void duplications_on_many_blocks() {
150 ComponentDto project1 = db.components().insertPrivateProject();
151 ComponentDto file1 = db.components().insertComponent(newFileDto(project1)
152 .setKey("org.codehaus.sonar:sonar-plugin-api:src/main/java/org/sonar/api/utils/command/CommandExecutor.java")
153 .setLongName("CommandExecutor"));
154 ComponentDto project2 = db.components().insertPrivateProject();
155 ComponentDto file2 = db.components().insertComponent(newFileDto(project2)
156 .setKey("com.sonarsource.orchestrator:sonar-orchestrator:src/main/java/com/sonar/orchestrator/util/CommandExecutor.java")
157 .setLongName("CommandExecutor"));
158 List<DuplicationsParser.Block> blocks = parser.parse(db.getSession(), file1, null, null,
159 format("<duplications>\n" +
161 " <b s=\"94\" l=\"101\" r=\"%s\"/>\n" +
162 " <b s=\"83\" l=\"101\" r=\"%s\"/>\n" +
165 " <b s=\"38\" l=\"40\" r=\"%s\"/>\n" +
166 " <b s=\"29\" l=\"39\" r=\"%s\"/>\n" +
168 "</duplications>\n", file2.getKey(), file1.getKey(), file2.getKey(), file1.getKey()));
169 assertThat(blocks).hasSize(2);
171 // Block with smaller line should come first
173 assertThat(blocks.get(0).getDuplications().get(0).from()).isEqualTo(29);
174 assertThat(blocks.get(0).getDuplications().get(1).from()).isEqualTo(38);
176 assertThat(blocks.get(1).getDuplications().get(0).from()).isEqualTo(83);
177 assertThat(blocks.get(1).getDuplications().get(1).from()).isEqualTo(94);
181 public void duplication_on_not_existing_file() {
182 ComponentDto project = db.components().insertPrivateProject();
183 ComponentDto file = db.components().insertComponent(newFileDto(project));
184 List<DuplicationsParser.Block> blocks = parser.parse(db.getSession(), file, null, null,
185 format("<duplications>\n" +
187 " <b s=\"20\" l=\"5\" r=\"%s\"/>\n" +
188 " <b s=\"31\" l=\"5\" r=\"%s\"/>\n" +
190 "</duplications>", file.getKey(), "not_existing"));
191 assertThat(blocks).hasSize(1);
193 List<Duplication> duplications = blocks.get(0).getDuplications();
194 assertThat(duplications).hasSize(2);
196 // Duplications on removed file
197 Duplication duplication1 = duplication(duplications, null);
198 assertThat(duplication1.componentDto()).isNull();
199 assertThat(duplication1.from()).isEqualTo(31);
200 assertThat(duplication1.size()).isEqualTo(5);
202 Duplication duplication2 = duplication(duplications, file.getKey());
203 assertThat(duplication2.componentDto()).isEqualTo(file);
204 assertThat(duplication2.from()).isEqualTo(20);
205 assertThat(duplication2.size()).isEqualTo(5);
209 public void compare_duplications() {
210 ComponentDto project1 = db.components().insertPrivateProject();
211 ComponentDto project2 = db.components().insertPrivateProject();
212 ComponentDto currentFile = db.components().insertComponent(newFileDto(project1, null));
213 ComponentDto fileOnSameProject = db.components().insertComponent(newFileDto(project1, null));
214 ComponentDto fileOnDifferentProject = db.components().insertComponent(newFileDto(project2, null));
216 DuplicationsParser.DuplicationComparator comparator = new DuplicationsParser.DuplicationComparator(currentFile.uuid(), currentFile.branchUuid());
219 assertThat(comparator.compare(Duplication.newComponent(currentFile, 2, 2),
220 Duplication.newComponent(currentFile, 5, 2))).isEqualTo(-1);
221 // Different files on same project
222 assertThat(comparator.compare(Duplication.newComponent(currentFile, 2, 2),
223 Duplication.newComponent(fileOnSameProject, 5, 2))).isEqualTo(-1);
224 assertThat(comparator.compare(Duplication.newComponent(fileOnSameProject, 2, 2),
225 Duplication.newComponent(currentFile, 5, 2))).isOne();
226 // Different files on different projects
227 assertThat(comparator.compare(Duplication.newComponent(fileOnSameProject, 5, 2),
228 Duplication.newComponent(fileOnDifferentProject, 2, 2))).isEqualTo(-1);
229 assertThat(comparator.compare(Duplication.newComponent(fileOnDifferentProject, 5, 2),
230 Duplication.newComponent(fileOnSameProject, 2, 2))).isOne();
231 // Files on 2 different projects
232 ComponentDto project3 = db.components().insertPrivateProject();
233 assertThat(comparator.compare(Duplication.newComponent(fileOnDifferentProject, 5, 2),
234 Duplication.newComponent(project3, 2, 2))).isOne();
236 // With null duplications
237 assertThat(comparator.compare(null, Duplication.newComponent(fileOnSameProject, 2, 2))).isEqualTo(-1);
238 assertThat(comparator.compare(Duplication.newComponent(fileOnSameProject, 2, 2), null)).isEqualTo(-1);
239 assertThat(comparator.compare(null, null)).isEqualTo(-1);
241 // On some removed file
242 assertThat(comparator.compare(Duplication.newComponent(currentFile, 2, 2),
243 Duplication.newRemovedComponent("key1", 5, 2))).isEqualTo(-1);
244 assertThat(comparator.compare(Duplication.newRemovedComponent("key2", 2, 2),
245 Duplication.newComponent(currentFile, 5, 2))).isOne();
249 public void duplication_on_branch() {
250 ComponentDto project = db.components().insertPublicProject();
251 ComponentDto branch = db.components().insertProjectBranch(project);
252 ComponentDto file1 = db.components().insertComponent(newFileDto(branch));
253 ComponentDto file2 = db.components().insertComponent(newFileDto(branch));
254 List<DuplicationsParser.Block> blocks = parser.parse(db.getSession(), file1, branch.getBranch(), null,
255 format("<duplications>\n" +
257 " <b s=\"20\" l=\"5\" r=\"%s\"/>\n" +
258 " <b s=\"31\" l=\"5\" r=\"%s\"/>\n" +
260 "</duplications>", file2.getKey(), file1.getKey()));
261 assertThat(blocks).hasSize(1);
263 List<Duplication> duplications = blocks.get(0).getDuplications();
264 assertThat(duplications).hasSize(2);
266 // Current file comes first
267 Duplication duplication1 = duplications.get(0);
268 assertThat(duplication1.componentDto()).isEqualTo(file1);
269 assertThat(duplication1.componentDto().getKey()).isEqualTo(file1.getKey());
270 assertThat(duplication1.from()).isEqualTo(31);
271 assertThat(duplication1.size()).isEqualTo(5);
273 Duplication duplication2 = duplications.get(1);
274 assertThat(duplication2.componentDto()).isEqualTo(file2);
275 assertThat(duplication2.componentDto().getKey()).isEqualTo(file2.getKey());
276 assertThat(duplication2.from()).isEqualTo(20);
277 assertThat(duplication2.size()).isEqualTo(5);
281 public void duplication_on_pull_request() {
282 ComponentDto project = db.components().insertPublicProject();
283 String pullRequestKey = RandomStringUtils.randomAlphanumeric(100);
284 ComponentDto pullRequest = db.components().insertProjectBranch(project, b -> b.setBranchType(BranchType.PULL_REQUEST).setKey(pullRequestKey));
285 ComponentDto file1 = db.components().insertComponent(newFileDto(pullRequest));
286 ComponentDto file2 = db.components().insertComponent(newFileDto(pullRequest));
287 List<DuplicationsParser.Block> blocks = parser.parse(db.getSession(), file1, null, pullRequestKey,
288 format("<duplications>\n" +
290 " <b s=\"20\" l=\"5\" r=\"%s\"/>\n" +
291 " <b s=\"31\" l=\"5\" r=\"%s\"/>\n" +
293 "</duplications>", file2.getKey(), file1.getKey()));
294 assertThat(blocks).hasSize(1);
296 List<Duplication> duplications = blocks.get(0).getDuplications();
297 assertThat(duplications).hasSize(2);
299 // Current file comes first
300 Duplication duplication1 = duplications.get(0);
301 assertThat(duplication1.componentDto()).isEqualTo(file1);
302 assertThat(duplication1.componentDto().getKey()).isEqualTo(file1.getKey());
303 assertThat(duplication1.from()).isEqualTo(31);
304 assertThat(duplication1.size()).isEqualTo(5);
306 Duplication duplication2 = duplications.get(1);
307 assertThat(duplication2.componentDto()).isEqualTo(file2);
308 assertThat(duplication2.componentDto().getKey()).isEqualTo(file2.getKey());
309 assertThat(duplication2.from()).isEqualTo(20);
310 assertThat(duplication2.size()).isEqualTo(5);
313 private static Duplication duplication(List<Duplication> duplications, @Nullable final String componentKey) {
314 return Iterables.find(duplications, input -> input != null && (componentKey == null ? input.componentDto() == null
315 : input.componentDto() != null && componentKey.equals(input.componentDto().getKey())));