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.junit.Rule;
26 import org.junit.Test;
27 import org.sonar.db.DbTester;
28 import org.sonar.db.component.BranchType;
29 import org.sonar.db.component.ComponentDto;
31 import static java.lang.String.format;
32 import static org.assertj.core.api.Assertions.assertThat;
33 import static org.sonar.db.component.ComponentTesting.newFileDto;
35 public class DuplicationsParserTest {
38 public DbTester db = DbTester.create();
40 private DuplicationsParser parser = new DuplicationsParser(db.getDbClient().componentDao());
43 public void empty_list_when_no_data() {
44 ComponentDto project = db.components().insertPrivateProject();
45 ComponentDto file = db.components().insertComponent(newFileDto(project));
47 assertThat(parser.parse(db.getSession(), file, null, null, null)).isEmpty();
51 public void duplication_on_same_file() {
52 ComponentDto project = db.components().insertPrivateProject();
53 ComponentDto file = db.components().insertComponent(newFileDto(project));
54 List<DuplicationsParser.Block> blocks = parser.parse(db.getSession(), file, null, null,
55 format("<duplications>\n" +
57 " <b s=\"31\" l=\"5\" r=\"%s\"/>\n" +
58 " <b s=\"20\" l=\"5\" r=\"%s\"/>\n" +
60 "</duplications>", file.getDbKey(), file.getDbKey()));
61 assertThat(blocks).hasSize(1);
63 List<Duplication> duplications = blocks.get(0).getDuplications();
64 assertThat(duplications).hasSize(2);
66 // Smallest line comes first
67 Duplication duplication1 = duplications.get(0);
68 assertThat(duplication1.componentDto()).isEqualTo(file);
69 assertThat(duplication1.from()).isEqualTo(20);
70 assertThat(duplication1.size()).isEqualTo(5);
72 Duplication duplication2 = duplications.get(1);
73 assertThat(duplication2.componentDto()).isEqualTo(file);
74 assertThat(duplication2.from()).isEqualTo(31);
75 assertThat(duplication2.size()).isEqualTo(5);
79 public void duplication_on_same_project() {
80 ComponentDto project = db.components().insertPrivateProject();
81 ComponentDto file1 = db.components().insertComponent(newFileDto(project));
82 ComponentDto file2 = db.components().insertComponent(newFileDto(project));
83 List<DuplicationsParser.Block> blocks = parser.parse(db.getSession(), file1, null, null,
84 format("<duplications>\n" +
86 " <b s=\"20\" l=\"5\" r=\"%s\"/>\n" +
87 " <b s=\"31\" l=\"5\" r=\"%s\"/>\n" +
89 "</duplications>", file2.getDbKey(), file1.getDbKey()));
90 assertThat(blocks).hasSize(1);
92 List<Duplication> duplications = blocks.get(0).getDuplications();
93 assertThat(duplications).hasSize(2);
95 // Current file comes first
96 Duplication duplication1 = duplications.get(0);
97 assertThat(duplication1.componentDto()).isEqualTo(file1);
98 assertThat(duplication1.from()).isEqualTo(31);
99 assertThat(duplication1.size()).isEqualTo(5);
101 Duplication duplication2 = duplications.get(1);
102 assertThat(duplication2.componentDto()).isEqualTo(file2);
103 assertThat(duplication2.from()).isEqualTo(20);
104 assertThat(duplication2.size()).isEqualTo(5);
108 public void duplications_on_different_project() {
109 ComponentDto project1 = db.components().insertPrivateProject();
110 ComponentDto file1 = db.components().insertComponent(newFileDto(project1));
111 ComponentDto file2 = db.components().insertComponent(newFileDto(project1));
112 ComponentDto project2 = db.components().insertPrivateProject();
113 ComponentDto fileOnProject2 = db.components().insertComponent(newFileDto(project2));
114 List<DuplicationsParser.Block> blocks = parser.parse(db.getSession(), file1, null, null,
115 format("<duplications>\n" +
117 " <b s=\"148\" l=\"24\" r=\"%s\"/>\n" +
118 " <b s=\"137\" l=\"24\" r=\"%s\"/>\n" +
119 " <b s=\"111\" l=\"24\" r=\"%s\"/>\n" +
121 "</duplications>", file1.getDbKey(), fileOnProject2.getDbKey(), file2.getDbKey()));
122 assertThat(blocks).hasSize(1);
124 List<Duplication> duplications = blocks.get(0).getDuplications();
125 assertThat(duplications).hasSize(3);
127 // Current file's project comes first
129 Duplication duplication1 = duplications.get(0);
130 assertThat(duplication1.componentDto()).isEqualTo(file1);
131 assertThat(duplication1.from()).isEqualTo(148);
132 assertThat(duplication1.size()).isEqualTo(24);
134 Duplication duplication2 = duplications.get(1);
135 assertThat(duplication2.componentDto()).isEqualTo(file2);
136 assertThat(duplication2.from()).isEqualTo(111);
137 assertThat(duplication2.size()).isEqualTo(24);
139 // Other project comes last
141 Duplication duplication3 = duplications.get(2);
142 assertThat(duplication3.componentDto()).isEqualTo(fileOnProject2);
143 assertThat(duplication3.from()).isEqualTo(137);
144 assertThat(duplication3.size()).isEqualTo(24);
148 public void duplications_on_many_blocks() {
149 ComponentDto project1 = db.components().insertPrivateProject();
150 ComponentDto file1 = db.components().insertComponent(newFileDto(project1)
151 .setDbKey("org.codehaus.sonar:sonar-plugin-api:src/main/java/org/sonar/api/utils/command/CommandExecutor.java")
152 .setLongName("CommandExecutor"));
153 ComponentDto project2 = db.components().insertPrivateProject();
154 ComponentDto file2 = db.components().insertComponent(newFileDto(project2)
155 .setDbKey("com.sonarsource.orchestrator:sonar-orchestrator:src/main/java/com/sonar/orchestrator/util/CommandExecutor.java")
156 .setLongName("CommandExecutor"));
157 List<DuplicationsParser.Block> blocks = parser.parse(db.getSession(), file1, null, null,
158 format("<duplications>\n" +
160 " <b s=\"94\" l=\"101\" r=\"%s\"/>\n" +
161 " <b s=\"83\" l=\"101\" r=\"%s\"/>\n" +
164 " <b s=\"38\" l=\"40\" r=\"%s\"/>\n" +
165 " <b s=\"29\" l=\"39\" r=\"%s\"/>\n" +
167 "</duplications>\n", file2.getDbKey(), file1.getDbKey(), file2.getDbKey(), file1.getDbKey()));
168 assertThat(blocks).hasSize(2);
170 // Block with smaller line should come first
172 assertThat(blocks.get(0).getDuplications().get(0).from()).isEqualTo(29);
173 assertThat(blocks.get(0).getDuplications().get(1).from()).isEqualTo(38);
175 assertThat(blocks.get(1).getDuplications().get(0).from()).isEqualTo(83);
176 assertThat(blocks.get(1).getDuplications().get(1).from()).isEqualTo(94);
180 public void duplication_on_not_existing_file() {
181 ComponentDto project = db.components().insertPrivateProject();
182 ComponentDto file = db.components().insertComponent(newFileDto(project));
183 List<DuplicationsParser.Block> blocks = parser.parse(db.getSession(), file, null, null,
184 format("<duplications>\n" +
186 " <b s=\"20\" l=\"5\" r=\"%s\"/>\n" +
187 " <b s=\"31\" l=\"5\" r=\"%s\"/>\n" +
189 "</duplications>", file.getDbKey(), "not_existing"));
190 assertThat(blocks).hasSize(1);
192 List<Duplication> duplications = blocks.get(0).getDuplications();
193 assertThat(duplications).hasSize(2);
195 // Duplications on removed file
196 Duplication duplication1 = duplication(duplications, null);
197 assertThat(duplication1.componentDto()).isNull();
198 assertThat(duplication1.from()).isEqualTo(31);
199 assertThat(duplication1.size()).isEqualTo(5);
201 Duplication duplication2 = duplication(duplications, file.getDbKey());
202 assertThat(duplication2.componentDto()).isEqualTo(file);
203 assertThat(duplication2.from()).isEqualTo(20);
204 assertThat(duplication2.size()).isEqualTo(5);
208 public void compare_duplications() {
209 ComponentDto project1 = db.components().insertPrivateProject();
210 ComponentDto project2 = db.components().insertPrivateProject();
211 ComponentDto currentFile = db.components().insertComponent(newFileDto(project1, null));
212 ComponentDto fileOnSameProject = db.components().insertComponent(newFileDto(project1, null));
213 ComponentDto fileOnDifferentProject = db.components().insertComponent(newFileDto(project2, null));
215 DuplicationsParser.DuplicationComparator comparator = new DuplicationsParser.DuplicationComparator(currentFile.uuid(), currentFile.projectUuid());
218 assertThat(comparator.compare(Duplication.newComponent(currentFile, 2, 2),
219 Duplication.newComponent(currentFile, 5, 2))).isEqualTo(-1);
220 // Different files on same project
221 assertThat(comparator.compare(Duplication.newComponent(currentFile, 2, 2),
222 Duplication.newComponent(fileOnSameProject, 5, 2))).isEqualTo(-1);
223 assertThat(comparator.compare(Duplication.newComponent(fileOnSameProject, 2, 2),
224 Duplication.newComponent(currentFile, 5, 2))).isOne();
225 // Different files on different projects
226 assertThat(comparator.compare(Duplication.newComponent(fileOnSameProject, 5, 2),
227 Duplication.newComponent(fileOnDifferentProject, 2, 2))).isEqualTo(-1);
228 assertThat(comparator.compare(Duplication.newComponent(fileOnDifferentProject, 5, 2),
229 Duplication.newComponent(fileOnSameProject, 2, 2))).isOne();
230 // Files on 2 different projects
231 ComponentDto project3 = db.components().insertPrivateProject();
232 assertThat(comparator.compare(Duplication.newComponent(fileOnDifferentProject, 5, 2),
233 Duplication.newComponent(project3, 2, 2))).isOne();
235 // With null duplications
236 assertThat(comparator.compare(null, Duplication.newComponent(fileOnSameProject, 2, 2))).isEqualTo(-1);
237 assertThat(comparator.compare(Duplication.newComponent(fileOnSameProject, 2, 2), null)).isEqualTo(-1);
238 assertThat(comparator.compare(null, null)).isEqualTo(-1);
240 // On some removed file
241 assertThat(comparator.compare(Duplication.newComponent(currentFile, 2, 2),
242 Duplication.newRemovedComponent("key1", 5, 2))).isEqualTo(-1);
243 assertThat(comparator.compare(Duplication.newRemovedComponent("key2", 2, 2),
244 Duplication.newComponent(currentFile, 5, 2))).isOne();
248 public void duplication_on_branch() {
249 ComponentDto project = db.components().insertPublicProject();
250 ComponentDto branch = db.components().insertProjectBranch(project);
251 ComponentDto file1 = db.components().insertComponent(newFileDto(branch));
252 ComponentDto file2 = db.components().insertComponent(newFileDto(branch));
253 List<DuplicationsParser.Block> blocks = parser.parse(db.getSession(), file1, branch.getBranch(), null,
254 format("<duplications>\n" +
256 " <b s=\"20\" l=\"5\" r=\"%s\"/>\n" +
257 " <b s=\"31\" l=\"5\" r=\"%s\"/>\n" +
259 "</duplications>", file2.getDbKey(), file1.getDbKey()));
260 assertThat(blocks).hasSize(1);
262 List<Duplication> duplications = blocks.get(0).getDuplications();
263 assertThat(duplications).hasSize(2);
265 // Current file comes first
266 Duplication duplication1 = duplications.get(0);
267 assertThat(duplication1.componentDto()).isEqualTo(file1);
268 assertThat(duplication1.componentDto().getKey()).isEqualTo(file1.getKey());
269 assertThat(duplication1.from()).isEqualTo(31);
270 assertThat(duplication1.size()).isEqualTo(5);
272 Duplication duplication2 = duplications.get(1);
273 assertThat(duplication2.componentDto()).isEqualTo(file2);
274 assertThat(duplication2.componentDto().getKey()).isEqualTo(file2.getKey());
275 assertThat(duplication2.from()).isEqualTo(20);
276 assertThat(duplication2.size()).isEqualTo(5);
280 public void duplication_on_pull_request() {
281 ComponentDto project = db.components().insertPublicProject();
282 ComponentDto pullRequest = db.components().insertProjectBranch(project, b -> b.setBranchType(BranchType.PULL_REQUEST));
283 ComponentDto file1 = db.components().insertComponent(newFileDto(pullRequest));
284 ComponentDto file2 = db.components().insertComponent(newFileDto(pullRequest));
285 List<DuplicationsParser.Block> blocks = parser.parse(db.getSession(), file1, null, pullRequest.getPullRequest(),
286 format("<duplications>\n" +
288 " <b s=\"20\" l=\"5\" r=\"%s\"/>\n" +
289 " <b s=\"31\" l=\"5\" r=\"%s\"/>\n" +
291 "</duplications>", file2.getDbKey(), file1.getDbKey()));
292 assertThat(blocks).hasSize(1);
294 List<Duplication> duplications = blocks.get(0).getDuplications();
295 assertThat(duplications).hasSize(2);
297 // Current file comes first
298 Duplication duplication1 = duplications.get(0);
299 assertThat(duplication1.componentDto()).isEqualTo(file1);
300 assertThat(duplication1.componentDto().getKey()).isEqualTo(file1.getKey());
301 assertThat(duplication1.from()).isEqualTo(31);
302 assertThat(duplication1.size()).isEqualTo(5);
304 Duplication duplication2 = duplications.get(1);
305 assertThat(duplication2.componentDto()).isEqualTo(file2);
306 assertThat(duplication2.componentDto().getKey()).isEqualTo(file2.getKey());
307 assertThat(duplication2.from()).isEqualTo(20);
308 assertThat(duplication2.size()).isEqualTo(5);
311 private static Duplication duplication(List<Duplication> duplications, @Nullable final String componentKey) {
312 return Iterables.find(duplications, input -> input != null && (componentKey == null ? input.componentDto() == null
313 : input.componentDto() != null && componentKey.equals(input.componentDto().getDbKey())));