3 * Copyright (C) 2009-2017 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.computation.task.projectanalysis.component;
22 import java.util.Arrays;
23 import java.util.EnumSet;
24 import java.util.HashMap;
26 import java.util.Objects;
27 import java.util.function.Function;
28 import javax.annotation.Nullable;
29 import org.junit.Rule;
30 import org.junit.Test;
31 import org.junit.rules.ExpectedException;
32 import org.junit.rules.ExternalResource;
33 import org.sonar.core.component.ComponentKeys;
34 import org.sonar.db.component.SnapshotDto;
35 import org.sonar.scanner.protocol.output.ScannerReport;
36 import org.sonar.server.computation.task.projectanalysis.analysis.Project;
38 import static com.google.common.base.Preconditions.checkArgument;
39 import static org.assertj.core.api.Assertions.assertThat;
40 import static org.junit.Assert.fail;
41 import static org.sonar.scanner.protocol.output.ScannerReport.Component.ComponentType.DIRECTORY;
42 import static org.sonar.scanner.protocol.output.ScannerReport.Component.ComponentType.FILE;
43 import static org.sonar.scanner.protocol.output.ScannerReport.Component.ComponentType.MODULE;
44 import static org.sonar.scanner.protocol.output.ScannerReport.Component.ComponentType.PROJECT;
45 import static org.sonar.scanner.protocol.output.ScannerReport.Component.ComponentType.UNRECOGNIZED;
46 import static org.sonar.scanner.protocol.output.ScannerReport.Component.newBuilder;
47 import static org.sonar.server.computation.task.projectanalysis.component.ComponentVisitor.Order.PRE_ORDER;
49 public class ComponentTreeBuilderTest {
51 private static final ComponentKeyGenerator KEY_GENERATOR = (module, component) -> "generated_"
52 + ComponentKeys.createEffectiveKey(module.getKey(), component != null ? component.getPath() : null);
53 private static final Function<String, String> UUID_SUPPLIER = (componentKey) -> componentKey + "_uuid";
54 private static final EnumSet<ScannerReport.Component.ComponentType> REPORT_TYPES = EnumSet.of(PROJECT, MODULE, DIRECTORY, FILE);
57 public ExpectedException expectedException = ExpectedException.none();
60 public ScannerComponentProvider scannerComponentProvider = new ScannerComponentProvider();
62 private Project projectInDb = new Project(UUID_SUPPLIER.apply("K1"), "K1", "theProjectName");
65 public void build_throws_IAE_for_all_types_but_PROJECT_MODULE_DIRECTORY_FILE() {
66 Arrays.stream(ScannerReport.Component.ComponentType.values())
67 .filter((type) -> type != UNRECOGNIZED)
68 .filter((type) -> !REPORT_TYPES.contains(type))
71 ScannerReport.Component component = newBuilder().setType(type).build();
74 fail("Should have thrown a IllegalArgumentException");
75 } catch (IllegalArgumentException e) {
76 assertThat(e).hasMessage("Unsupported component type '" + type + "'");
82 public void by_default_project_is_loaded_from_report() {
83 String nameInReport = "the name";
84 String descriptionInReport = "the desc";
85 Component root = call(newBuilder()
87 .setKey(projectInDb.getKey())
89 .setName(nameInReport)
90 .setDescription(descriptionInReport)
94 assertThat(root.getUuid()).isEqualTo("generated_K1_uuid");
95 assertThat(root.getKey()).isEqualTo("generated_K1");
96 assertThat(root.getType()).isEqualTo(Component.Type.PROJECT);
97 assertThat(root.getName()).isEqualTo(nameInReport);
98 assertThat(root.getDescription()).isEqualTo(descriptionInReport);
99 assertThat(root.getReportAttributes().getRef()).isEqualTo(42);
100 assertThat(root.getReportAttributes().getPath()).isNull();
101 assertThat(root.getReportAttributes().getVersion()).isEqualTo("6.5");
102 assertThatFileAttributesAreNotSet(root);
106 public void project_name_is_loaded_from_db_if_absent_from_report() {
107 Component root = call(newBuilder()
111 assertThat(root.getName()).isEqualTo(projectInDb.getName());
115 public void project_version_is_loaded_from_db_if_absent_from_report() {
116 SnapshotDto baseAnalysis = new SnapshotDto().setVersion("6.5");
117 Component root = call(newBuilder()
119 .build(), baseAnalysis);
121 assertThat(root.getReportAttributes().getVersion()).isEqualTo("6.5");
125 public void project_version_is_loaded_from_db_if_empty_report() {
126 SnapshotDto baseAnalysis = new SnapshotDto().setVersion("6.5");
127 Component root = call(newBuilder()
130 .build(), baseAnalysis);
132 assertThat(root.getReportAttributes().getVersion()).isEqualTo("6.5");
136 public void project_version_is_hardcoded_if_absent_from_report_and_db() {
137 Component root = call(newBuilder()
141 assertThat(root.getReportAttributes().getVersion()).isEqualTo("not provided");
145 public void project_description_is_null_if_absent_from_report() {
146 Component root = call(newBuilder()
150 assertThat(root.getDescription()).isNull();
154 public void keys_of_module_directory_and_file_are_generated() {
155 ScannerReport.Component project = newBuilder()
157 .setKey(projectInDb.getKey())
161 scannerComponentProvider.add(newBuilder()
166 scannerComponentProvider.add(newBuilder()
171 scannerComponentProvider.add(newBuilder()
174 .setPath("src/js/Foo.js")
177 Component root = call(project);
178 assertThat(root.getKey()).isEqualTo("generated_" + projectInDb.getKey());
179 assertThat(root.getChildren()).hasSize(1);
181 Component module = root.getChildren().iterator().next();
182 assertThat(module.getKey()).isEqualTo("generated_M");
183 assertThat(module.getChildren()).hasSize(1);
185 Component directory = module.getChildren().iterator().next();
186 assertThat(directory.getKey()).isEqualTo("generated_M:src/js");
187 assertThat(directory.getChildren()).hasSize(1);
189 Component file = directory.getChildren().iterator().next();
190 assertThat(file.getKey()).isEqualTo("generated_M:src/js/Foo.js");
191 assertThat(file.getChildren()).isEmpty();
195 public void names_of_module_directory_and_file_are_keys_if_names_are_absent_from_report() {
196 ScannerReport.Component project = newBuilder()
198 .setKey(projectInDb.getKey())
202 scannerComponentProvider.add(newBuilder()
207 scannerComponentProvider.add(newBuilder()
212 scannerComponentProvider.add(newBuilder()
215 .setPath("src/js/Foo.js")
218 Component root = call(project);
220 Component module = root.getChildren().iterator().next();
221 assertThat(module.getName()).isEqualTo("generated_M");
223 Component directory = module.getChildren().iterator().next();
224 assertThat(directory.getName()).isEqualTo("generated_M:src/js");
226 Component file = directory.getChildren().iterator().next();
227 assertThat(file.getName()).isEqualTo("generated_M:src/js/Foo.js");
231 public void names_of_module_directory_and_file_are_keys_if_names_are_empty_in_report() {
232 ScannerReport.Component project = newBuilder()
234 .setKey(projectInDb.getKey())
238 scannerComponentProvider.add(newBuilder()
244 scannerComponentProvider.add(newBuilder()
250 scannerComponentProvider.add(newBuilder()
253 .setPath("src/js/Foo.js")
257 Component root = call(project);
259 Component module = root.getChildren().iterator().next();
260 assertThat(module.getName()).isEqualTo("generated_M");
262 Component directory = module.getChildren().iterator().next();
263 assertThat(directory.getName()).isEqualTo("generated_M:src/js");
265 Component file = directory.getChildren().iterator().next();
266 assertThat(file.getName()).isEqualTo("generated_M:src/js/Foo.js");
269 private void assertThatFileAttributesAreNotSet(Component root) {
271 root.getFileAttributes();
273 } catch (IllegalStateException e) {
274 assertThat(e).hasMessage("Only component of type FILE have a FileAttributes object");
279 public void name_of_module_directory_and_files_includes_name_of_closest_module() {
280 ScannerReport.Component project = newBuilder()
284 .addChildRef(11).addChildRef(21).addChildRef(31).build();
285 scannerComponentProvider.add(newBuilder().setRef(11).setType(MODULE).setKey("module 1").addChildRef(12).addChildRef(22).addChildRef(32));
286 scannerComponentProvider.add(newBuilder().setRef(12).setType(MODULE).setKey("module 2").addChildRef(13).addChildRef(23).addChildRef(33));
287 scannerComponentProvider.add(newBuilder().setRef(13).setType(MODULE).setKey("module 3").addChildRef(24).addChildRef(34));
288 scannerComponentProvider.add(newBuilder().setRef(21).setType(DIRECTORY).setPath("directory in project").addChildRef(35));
289 scannerComponentProvider.add(newBuilder().setRef(22).setType(DIRECTORY).setPath("directory in module 1").addChildRef(36));
290 scannerComponentProvider.add(newBuilder().setRef(23).setType(DIRECTORY).setPath("directory in module 2").addChildRef(37));
291 scannerComponentProvider.add(newBuilder().setRef(24).setType(DIRECTORY).setPath("directory in module 3").addChildRef(38));
292 scannerComponentProvider.add(newBuilder().setRef(31).setType(FILE).setPath("file in project").setLines(1));
293 scannerComponentProvider.add(newBuilder().setRef(32).setType(FILE).setPath("file in module 1").setLines(1));
294 scannerComponentProvider.add(newBuilder().setRef(33).setType(FILE).setPath("file in module 2").setLines(1));
295 scannerComponentProvider.add(newBuilder().setRef(34).setType(FILE).setPath("file in module 3").setLines(1));
296 scannerComponentProvider.add(newBuilder().setRef(35).setType(FILE).setPath("file in directory in project").setLines(1));
297 scannerComponentProvider.add(newBuilder().setRef(36).setType(FILE).setPath("file in directory in module 1").setLines(1));
298 scannerComponentProvider.add(newBuilder().setRef(37).setType(FILE).setPath("file in directory in module 2").setLines(1));
299 scannerComponentProvider.add(newBuilder().setRef(38).setType(FILE).setPath("file in directory in module 3").setLines(1));
301 Component root = call(project);
302 Map<Integer, Component> componentsByRef = indexComponentByRef(root);
303 assertThat(componentsByRef.get(11).getKey()).isEqualTo("generated_module 1");
304 assertThat(componentsByRef.get(12).getKey()).isEqualTo("generated_module 2");
305 assertThat(componentsByRef.get(13).getKey()).isEqualTo("generated_module 3");
306 assertThat(componentsByRef.get(21).getKey()).startsWith("generated_project 1:");
307 assertThat(componentsByRef.get(22).getKey()).startsWith("generated_module 1:");
308 assertThat(componentsByRef.get(23).getKey()).startsWith("generated_module 2:");
309 assertThat(componentsByRef.get(24).getKey()).startsWith("generated_module 3:");
310 assertThat(componentsByRef.get(31).getKey()).startsWith("generated_project 1:");
311 assertThat(componentsByRef.get(32).getKey()).startsWith("generated_module 1:");
312 assertThat(componentsByRef.get(33).getKey()).startsWith("generated_module 2:");
313 assertThat(componentsByRef.get(34).getKey()).startsWith("generated_module 3:");
314 assertThat(componentsByRef.get(35).getKey()).startsWith("generated_project 1:");
315 assertThat(componentsByRef.get(36).getKey()).startsWith("generated_module 1:");
316 assertThat(componentsByRef.get(37).getKey()).startsWith("generated_module 2:");
317 assertThat(componentsByRef.get(38).getKey()).startsWith("generated_module 3:");
321 public void uuids_are_provided_by_supplier() {
322 ScannerReport.Component project = newBuilder()
328 scannerComponentProvider.add(newBuilder()
333 scannerComponentProvider.add(newBuilder()
338 scannerComponentProvider.add(newBuilder()
341 .setPath("src/js/Foo.js")
344 Component root = call(project);
345 assertThat(root.getUuid()).isEqualTo("generated_c1_uuid");
347 Component module = root.getChildren().iterator().next();
348 assertThat(module.getUuid()).isEqualTo("generated_c2_uuid");
350 Component directory = module.getChildren().iterator().next();
351 assertThat(directory.getUuid()).isEqualTo("generated_c2:src/js_uuid");
353 Component file = directory.getChildren().iterator().next();
354 assertThat(file.getUuid()).isEqualTo("generated_c2:src/js/Foo.js_uuid");
358 public void descriptions_of_module_directory_and_file_are_null_if_absent_from_report() {
359 ScannerReport.Component project = newBuilder()
364 scannerComponentProvider.add(newBuilder()
368 scannerComponentProvider.add(newBuilder()
373 scannerComponentProvider.add(newBuilder()
376 .setPath("src/js/Foo.js")
379 Component root = call(project);
381 Component module = root.getChildren().iterator().next();
382 assertThat(module.getDescription()).isNull();
384 Component directory = module.getChildren().iterator().next();
385 assertThat(directory.getDescription()).isNull();
387 Component file = directory.getChildren().iterator().next();
388 assertThat(file.getDescription()).isNull();
392 public void descriptions_of_module_directory_and_file_are_null_if_empty_in_report() {
393 ScannerReport.Component project = newBuilder()
399 scannerComponentProvider.add(newBuilder()
404 scannerComponentProvider.add(newBuilder()
410 scannerComponentProvider.add(newBuilder()
414 .setPath("src/js/Foo.js")
417 Component root = call(project);
419 Component module = root.getChildren().iterator().next();
420 assertThat(module.getDescription()).isNull();
422 Component directory = module.getChildren().iterator().next();
423 assertThat(directory.getDescription()).isNull();
425 Component file = directory.getChildren().iterator().next();
426 assertThat(file.getDescription()).isNull();
430 public void descriptions_of_module_directory_and_file_are_set_from_report_if_present() {
431 ScannerReport.Component project = newBuilder()
436 scannerComponentProvider.add(newBuilder()
441 scannerComponentProvider.add(newBuilder()
447 scannerComponentProvider.add(newBuilder()
451 .setPath("src/js/Foo.js")
454 Component root = call(project);
456 Component module = root.getChildren().iterator().next();
457 assertThat(module.getDescription()).isEqualTo("b");
459 Component directory = module.getChildren().iterator().next();
460 assertThat(directory.getDescription()).isEqualTo("c");
462 Component file = directory.getChildren().iterator().next();
463 assertThat(file.getDescription()).isEqualTo("d");
467 public void versions_of_module_directory_and_file_are_set_from_report_if_present() {
468 ScannerReport.Component project = newBuilder()
473 scannerComponentProvider.add(newBuilder()
478 scannerComponentProvider.add(newBuilder()
484 scannerComponentProvider.add(newBuilder()
488 .setPath("src/js/Foo.js")
491 Component root = call(project);
493 Component module = root.getChildren().iterator().next();
494 assertThat(module.getReportAttributes().getVersion()).isEqualTo("v1");
496 Component directory = module.getChildren().iterator().next();
497 assertThat(directory.getReportAttributes().getVersion()).isEqualTo("v2");
499 Component file = directory.getChildren().iterator().next();
500 assertThat(file.getReportAttributes().getVersion()).isEqualTo("v3");
504 public void versions_of_module_directory_and_file_are_null_if_absent_from_report() {
505 ScannerReport.Component project = newBuilder()
510 scannerComponentProvider.add(newBuilder()
514 scannerComponentProvider.add(newBuilder()
519 scannerComponentProvider.add(newBuilder()
522 .setPath("src/js/Foo.js")
525 Component root = call(project);
527 Component module = root.getChildren().iterator().next();
528 assertThat(module.getReportAttributes().getVersion()).isNull();
530 Component directory = module.getChildren().iterator().next();
531 assertThat(directory.getReportAttributes().getVersion()).isNull();
533 Component file = directory.getChildren().iterator().next();
534 assertThat(file.getReportAttributes().getVersion()).isNull();
538 public void versions_of_module_directory_and_file_are_null_if_empty_in_report() {
539 ScannerReport.Component project = newBuilder()
544 scannerComponentProvider.add(newBuilder()
549 scannerComponentProvider.add(newBuilder()
555 scannerComponentProvider.add(newBuilder()
559 .setPath("src/js/Foo.js")
562 Component root = call(project);
564 Component module = root.getChildren().iterator().next();
565 assertThat(module.getReportAttributes().getVersion()).isNull();
567 Component directory = module.getChildren().iterator().next();
568 assertThat(directory.getReportAttributes().getVersion()).isNull();
570 Component file = directory.getChildren().iterator().next();
571 assertThat(file.getReportAttributes().getVersion()).isNull();
575 public void only_nb_of_lines_is_mandatory_on_file_attributes() {
576 ScannerReport.Component project = newBuilder()
581 scannerComponentProvider.add(newBuilder()
584 .setPath("src/js/Foo.js")
587 Component root = call(project);
588 Component file = root.getChildren().iterator().next();
589 assertThat(file.getFileAttributes().getLines()).isEqualTo(1);
590 assertThat(file.getFileAttributes().getLanguageKey()).isNull();
591 assertThat(file.getFileAttributes().isUnitTest()).isFalse();
595 public void language_file_attributes_is_null_if_empty_in_report() {
596 ScannerReport.Component project = newBuilder()
601 scannerComponentProvider.add(newBuilder()
604 .setPath("src/js/Foo.js")
608 Component root = call(project);
609 Component file = root.getChildren().iterator().next();
610 assertThat(file.getFileAttributes().getLanguageKey()).isNull();
614 public void file_attributes_are_fully_loaded_from_report() {
615 ScannerReport.Component project = newBuilder()
620 scannerComponentProvider.add(newBuilder()
623 .setPath("src/js/Foo.js")
628 Component root = call(project);
629 Component file = root.getChildren().iterator().next();
630 assertThat(file.getFileAttributes().getLines()).isEqualTo(1);
631 assertThat(file.getFileAttributes().getLanguageKey()).isEqualTo("js");
632 assertThat(file.getFileAttributes().isUnitTest()).isTrue();
636 public void throw_IAE_if_lines_is_absent_from_report() {
637 ScannerReport.Component project = newBuilder()
642 scannerComponentProvider.add(newBuilder()
645 .setPath("src/js/Foo.js"));
647 expectedException.expect(IllegalArgumentException.class);
648 expectedException.expectMessage("File 'src/js/Foo.js' has no line");
654 public void throw_IAE_if_lines_is_zero_in_report() {
655 ScannerReport.Component project = newBuilder()
660 scannerComponentProvider.add(newBuilder()
663 .setPath("src/js/Foo.js")
666 expectedException.expect(IllegalArgumentException.class);
667 expectedException.expectMessage("File 'src/js/Foo.js' has no line");
673 public void throw_IAE_if_lines_is_negative_in_report() {
674 ScannerReport.Component project = newBuilder()
679 scannerComponentProvider.add(newBuilder()
682 .setPath("src/js/Foo.js")
685 expectedException.expect(IllegalArgumentException.class);
686 expectedException.expectMessage("File 'src/js/Foo.js' has no line");
691 private static class ScannerComponentProvider extends ExternalResource implements Function<Integer, ScannerReport.Component> {
692 private final Map<Integer, ScannerReport.Component> components = new HashMap<>();
695 protected void before() throws Throwable {
700 public ScannerReport.Component apply(Integer componentRef) {
701 return Objects.requireNonNull(components.get(componentRef), "No Component for componentRef " + componentRef);
704 public ScannerReport.Component add(ScannerReport.Component.Builder builder) {
705 ScannerReport.Component component = builder.build();
706 ScannerReport.Component existing = components.put(component.getRef(), component);
707 checkArgument(existing == null, "Component %s already set for ref %s", existing, component.getRef());
712 private Component call(ScannerReport.Component project) {
713 return newUnderTest(null).buildProject(project);
716 private Component call(ScannerReport.Component project, @Nullable SnapshotDto baseAnalysis) {
717 return newUnderTest(baseAnalysis).buildProject(project);
720 private ComponentTreeBuilder newUnderTest(@Nullable SnapshotDto baseAnalysis) {
721 return new ComponentTreeBuilder(KEY_GENERATOR, UUID_SUPPLIER, scannerComponentProvider, projectInDb, baseAnalysis);
724 private static Map<Integer, Component> indexComponentByRef(Component root) {
725 Map<Integer, Component> componentsByRef = new HashMap<>();
726 new DepthTraversalTypeAwareCrawler(
727 new TypeAwareVisitorAdapter(CrawlerDepthLimit.FILE, PRE_ORDER) {
729 public void visitAny(Component any) {
730 componentsByRef.put(any.getReportAttributes().getRef(), any);
733 return componentsByRef;