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.newBuilder;
42 import static org.sonar.scanner.protocol.output.ScannerReport.Component.ComponentType.DIRECTORY;
43 import static org.sonar.scanner.protocol.output.ScannerReport.Component.ComponentType.FILE;
44 import static org.sonar.scanner.protocol.output.ScannerReport.Component.ComponentType.MODULE;
45 import static org.sonar.scanner.protocol.output.ScannerReport.Component.ComponentType.PROJECT;
46 import static org.sonar.scanner.protocol.output.ScannerReport.Component.ComponentType.UNRECOGNIZED;
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 ComponentKeyGenerator PUBLIC_KEY_GENERATOR = (module, component) -> "public_"
54 + ComponentKeys.createEffectiveKey(module.getKey(), component != null ? component.getPath() : null);
55 private static final Function<String, String> UUID_SUPPLIER = (componentKey) -> componentKey + "_uuid";
56 private static final EnumSet<ScannerReport.Component.ComponentType> REPORT_TYPES = EnumSet.of(PROJECT, MODULE, DIRECTORY, FILE);
59 public ExpectedException expectedException = ExpectedException.none();
62 public ScannerComponentProvider scannerComponentProvider = new ScannerComponentProvider();
64 private Project projectInDb = new Project(UUID_SUPPLIER.apply("K1"), "K1", "theProjectName");
67 public void build_throws_IAE_for_all_types_but_PROJECT_MODULE_DIRECTORY_FILE() {
68 Arrays.stream(ScannerReport.Component.ComponentType.values())
69 .filter((type) -> type != UNRECOGNIZED)
70 .filter((type) -> !REPORT_TYPES.contains(type))
73 ScannerReport.Component component = newBuilder().setType(type).build();
76 fail("Should have thrown a IllegalArgumentException");
77 } catch (IllegalArgumentException e) {
78 assertThat(e).hasMessage("Unsupported component type '" + type + "'");
84 public void by_default_project_is_loaded_from_report() {
85 String nameInReport = "the name";
86 String descriptionInReport = "the desc";
87 Component root = call(newBuilder()
89 .setKey(projectInDb.getKey())
91 .setName(nameInReport)
92 .setDescription(descriptionInReport)
96 assertThat(root.getUuid()).isEqualTo("generated_K1_uuid");
97 assertThat(root.getKey()).isEqualTo("generated_K1");
98 assertThat(root.getPublicKey()).isEqualTo("public_K1");
99 assertThat(root.getType()).isEqualTo(Component.Type.PROJECT);
100 assertThat(root.getName()).isEqualTo(nameInReport);
101 assertThat(root.getDescription()).isEqualTo(descriptionInReport);
102 assertThat(root.getReportAttributes().getRef()).isEqualTo(42);
103 assertThat(root.getReportAttributes().getPath()).isNull();
104 assertThat(root.getReportAttributes().getVersion()).isEqualTo("6.5");
105 assertThatFileAttributesAreNotSet(root);
109 public void project_name_is_loaded_from_db_if_absent_from_report() {
110 Component root = call(newBuilder()
114 assertThat(root.getName()).isEqualTo(projectInDb.getName());
118 public void project_version_is_loaded_from_db_if_absent_from_report() {
119 SnapshotDto baseAnalysis = new SnapshotDto().setVersion("6.5");
120 Component root = call(newBuilder()
122 .build(), baseAnalysis);
124 assertThat(root.getReportAttributes().getVersion()).isEqualTo("6.5");
128 public void project_version_is_loaded_from_db_if_empty_report() {
129 SnapshotDto baseAnalysis = new SnapshotDto().setVersion("6.5");
130 Component root = call(newBuilder()
133 .build(), baseAnalysis);
135 assertThat(root.getReportAttributes().getVersion()).isEqualTo("6.5");
139 public void project_version_is_hardcoded_if_absent_from_report_and_db() {
140 Component root = call(newBuilder()
144 assertThat(root.getReportAttributes().getVersion()).isEqualTo("not provided");
148 public void project_description_is_null_if_absent_from_report() {
149 Component root = call(newBuilder()
153 assertThat(root.getDescription()).isNull();
157 public void keys_of_module_directory_and_file_are_generated() {
158 ScannerReport.Component project = newBuilder()
160 .setKey(projectInDb.getKey())
164 scannerComponentProvider.add(newBuilder()
169 scannerComponentProvider.add(newBuilder()
174 scannerComponentProvider.add(newBuilder()
177 .setPath("src/js/Foo.js")
180 Component root = call(project);
181 assertThat(root.getKey()).isEqualTo("generated_" + projectInDb.getKey());
182 assertThat(root.getPublicKey()).isEqualTo("public_" + projectInDb.getKey());
183 assertThat(root.getChildren()).hasSize(1);
185 Component module = root.getChildren().iterator().next();
186 assertThat(module.getKey()).isEqualTo("generated_M");
187 assertThat(module.getPublicKey()).isEqualTo("public_M");
188 assertThat(module.getChildren()).hasSize(1);
190 Component directory = module.getChildren().iterator().next();
191 assertThat(directory.getKey()).isEqualTo("generated_M:src/js");
192 assertThat(directory.getPublicKey()).isEqualTo("public_M:src/js");
193 assertThat(directory.getChildren()).hasSize(1);
195 Component file = directory.getChildren().iterator().next();
196 assertThat(file.getKey()).isEqualTo("generated_M:src/js/Foo.js");
197 assertThat(file.getPublicKey()).isEqualTo("public_M:src/js/Foo.js");
198 assertThat(file.getChildren()).isEmpty();
202 public void names_of_module_directory_and_file_are_public_keys_if_names_are_absent_from_report() {
203 ScannerReport.Component project = newBuilder()
205 .setKey(projectInDb.getKey())
209 scannerComponentProvider.add(newBuilder()
214 scannerComponentProvider.add(newBuilder()
219 scannerComponentProvider.add(newBuilder()
222 .setPath("src/js/Foo.js")
225 Component root = call(project);
227 Component module = root.getChildren().iterator().next();
228 assertThat(module.getName()).isEqualTo("generated_M");
230 Component directory = module.getChildren().iterator().next();
231 assertThat(directory.getName()).isEqualTo("generated_M:src/js");
233 Component file = directory.getChildren().iterator().next();
234 assertThat(file.getName()).isEqualTo("generated_M:src/js/Foo.js");
238 public void names_of_module_directory_and_file_are_keys_if_names_are_empty_in_report() {
239 ScannerReport.Component project = newBuilder()
241 .setKey(projectInDb.getKey())
245 scannerComponentProvider.add(newBuilder()
251 scannerComponentProvider.add(newBuilder()
257 scannerComponentProvider.add(newBuilder()
260 .setPath("src/js/Foo.js")
264 Component root = call(project);
266 Component module = root.getChildren().iterator().next();
267 assertThat(module.getName()).isEqualTo("generated_M");
269 Component directory = module.getChildren().iterator().next();
270 assertThat(directory.getName()).isEqualTo("generated_M:src/js");
272 Component file = directory.getChildren().iterator().next();
273 assertThat(file.getName()).isEqualTo("generated_M:src/js/Foo.js");
276 private void assertThatFileAttributesAreNotSet(Component root) {
278 root.getFileAttributes();
280 } catch (IllegalStateException e) {
281 assertThat(e).hasMessage("Only component of type FILE have a FileAttributes object");
286 public void keys_of_module_directory_and_files_includes_name_of_closest_module() {
287 ScannerReport.Component project = newBuilder()
291 .addChildRef(11).addChildRef(21).addChildRef(31).build();
292 scannerComponentProvider.add(newBuilder().setRef(11).setType(MODULE).setKey("module 1").addChildRef(12).addChildRef(22).addChildRef(32));
293 scannerComponentProvider.add(newBuilder().setRef(12).setType(MODULE).setKey("module 2").addChildRef(13).addChildRef(23).addChildRef(33));
294 scannerComponentProvider.add(newBuilder().setRef(13).setType(MODULE).setKey("module 3").addChildRef(24).addChildRef(34));
295 scannerComponentProvider.add(newBuilder().setRef(21).setType(DIRECTORY).setPath("directory in project").addChildRef(35));
296 scannerComponentProvider.add(newBuilder().setRef(22).setType(DIRECTORY).setPath("directory in module 1").addChildRef(36));
297 scannerComponentProvider.add(newBuilder().setRef(23).setType(DIRECTORY).setPath("directory in module 2").addChildRef(37));
298 scannerComponentProvider.add(newBuilder().setRef(24).setType(DIRECTORY).setPath("directory in module 3").addChildRef(38));
299 scannerComponentProvider.add(newBuilder().setRef(31).setType(FILE).setPath("file in project").setLines(1));
300 scannerComponentProvider.add(newBuilder().setRef(32).setType(FILE).setPath("file in module 1").setLines(1));
301 scannerComponentProvider.add(newBuilder().setRef(33).setType(FILE).setPath("file in module 2").setLines(1));
302 scannerComponentProvider.add(newBuilder().setRef(34).setType(FILE).setPath("file in module 3").setLines(1));
303 scannerComponentProvider.add(newBuilder().setRef(35).setType(FILE).setPath("file in directory in project").setLines(1));
304 scannerComponentProvider.add(newBuilder().setRef(36).setType(FILE).setPath("file in directory in module 1").setLines(1));
305 scannerComponentProvider.add(newBuilder().setRef(37).setType(FILE).setPath("file in directory in module 2").setLines(1));
306 scannerComponentProvider.add(newBuilder().setRef(38).setType(FILE).setPath("file in directory in module 3").setLines(1));
308 Component root = call(project);
309 Map<Integer, Component> componentsByRef = indexComponentByRef(root);
310 assertThat(componentsByRef.get(11).getKey()).isEqualTo("generated_module 1");
311 assertThat(componentsByRef.get(11).getPublicKey()).isEqualTo("public_module 1");
312 assertThat(componentsByRef.get(12).getKey()).isEqualTo("generated_module 2");
313 assertThat(componentsByRef.get(12).getPublicKey()).isEqualTo("public_module 2");
314 assertThat(componentsByRef.get(13).getKey()).isEqualTo("generated_module 3");
315 assertThat(componentsByRef.get(13).getPublicKey()).isEqualTo("public_module 3");
316 assertThat(componentsByRef.get(21).getKey()).startsWith("generated_project 1:");
317 assertThat(componentsByRef.get(21).getPublicKey()).startsWith("public_project 1:");
318 assertThat(componentsByRef.get(22).getKey()).startsWith("generated_module 1:");
319 assertThat(componentsByRef.get(22).getPublicKey()).startsWith("public_module 1:");
320 assertThat(componentsByRef.get(23).getKey()).startsWith("generated_module 2:");
321 assertThat(componentsByRef.get(23).getPublicKey()).startsWith("public_module 2:");
322 assertThat(componentsByRef.get(24).getKey()).startsWith("generated_module 3:");
323 assertThat(componentsByRef.get(24).getPublicKey()).startsWith("public_module 3:");
324 assertThat(componentsByRef.get(31).getKey()).startsWith("generated_project 1:");
325 assertThat(componentsByRef.get(31).getPublicKey()).startsWith("public_project 1:");
326 assertThat(componentsByRef.get(32).getKey()).startsWith("generated_module 1:");
327 assertThat(componentsByRef.get(32).getPublicKey()).startsWith("public_module 1:");
328 assertThat(componentsByRef.get(33).getKey()).startsWith("generated_module 2:");
329 assertThat(componentsByRef.get(33).getPublicKey()).startsWith("public_module 2:");
330 assertThat(componentsByRef.get(34).getKey()).startsWith("generated_module 3:");
331 assertThat(componentsByRef.get(34).getPublicKey()).startsWith("public_module 3:");
332 assertThat(componentsByRef.get(35).getKey()).startsWith("generated_project 1:");
333 assertThat(componentsByRef.get(35).getPublicKey()).startsWith("public_project 1:");
334 assertThat(componentsByRef.get(36).getKey()).startsWith("generated_module 1:");
335 assertThat(componentsByRef.get(36).getPublicKey()).startsWith("public_module 1:");
336 assertThat(componentsByRef.get(37).getKey()).startsWith("generated_module 2:");
337 assertThat(componentsByRef.get(37).getPublicKey()).startsWith("public_module 2:");
338 assertThat(componentsByRef.get(38).getKey()).startsWith("generated_module 3:");
339 assertThat(componentsByRef.get(38).getPublicKey()).startsWith("public_module 3:");
343 public void uuids_are_provided_by_supplier() {
344 ScannerReport.Component project = newBuilder()
350 scannerComponentProvider.add(newBuilder()
355 scannerComponentProvider.add(newBuilder()
360 scannerComponentProvider.add(newBuilder()
363 .setPath("src/js/Foo.js")
366 Component root = call(project);
367 assertThat(root.getUuid()).isEqualTo("generated_c1_uuid");
369 Component module = root.getChildren().iterator().next();
370 assertThat(module.getUuid()).isEqualTo("generated_c2_uuid");
372 Component directory = module.getChildren().iterator().next();
373 assertThat(directory.getUuid()).isEqualTo("generated_c2:src/js_uuid");
375 Component file = directory.getChildren().iterator().next();
376 assertThat(file.getUuid()).isEqualTo("generated_c2:src/js/Foo.js_uuid");
380 public void descriptions_of_module_directory_and_file_are_null_if_absent_from_report() {
381 ScannerReport.Component project = newBuilder()
386 scannerComponentProvider.add(newBuilder()
390 scannerComponentProvider.add(newBuilder()
395 scannerComponentProvider.add(newBuilder()
398 .setPath("src/js/Foo.js")
401 Component root = call(project);
403 Component module = root.getChildren().iterator().next();
404 assertThat(module.getDescription()).isNull();
406 Component directory = module.getChildren().iterator().next();
407 assertThat(directory.getDescription()).isNull();
409 Component file = directory.getChildren().iterator().next();
410 assertThat(file.getDescription()).isNull();
414 public void descriptions_of_module_directory_and_file_are_null_if_empty_in_report() {
415 ScannerReport.Component project = newBuilder()
421 scannerComponentProvider.add(newBuilder()
426 scannerComponentProvider.add(newBuilder()
432 scannerComponentProvider.add(newBuilder()
436 .setPath("src/js/Foo.js")
439 Component root = call(project);
441 Component module = root.getChildren().iterator().next();
442 assertThat(module.getDescription()).isNull();
444 Component directory = module.getChildren().iterator().next();
445 assertThat(directory.getDescription()).isNull();
447 Component file = directory.getChildren().iterator().next();
448 assertThat(file.getDescription()).isNull();
452 public void descriptions_of_module_directory_and_file_are_set_from_report_if_present() {
453 ScannerReport.Component project = newBuilder()
458 scannerComponentProvider.add(newBuilder()
463 scannerComponentProvider.add(newBuilder()
469 scannerComponentProvider.add(newBuilder()
473 .setPath("src/js/Foo.js")
476 Component root = call(project);
478 Component module = root.getChildren().iterator().next();
479 assertThat(module.getDescription()).isEqualTo("b");
481 Component directory = module.getChildren().iterator().next();
482 assertThat(directory.getDescription()).isEqualTo("c");
484 Component file = directory.getChildren().iterator().next();
485 assertThat(file.getDescription()).isEqualTo("d");
489 public void versions_of_module_directory_and_file_are_set_from_report_if_present() {
490 ScannerReport.Component project = newBuilder()
495 scannerComponentProvider.add(newBuilder()
500 scannerComponentProvider.add(newBuilder()
506 scannerComponentProvider.add(newBuilder()
510 .setPath("src/js/Foo.js")
513 Component root = call(project);
515 Component module = root.getChildren().iterator().next();
516 assertThat(module.getReportAttributes().getVersion()).isEqualTo("v1");
518 Component directory = module.getChildren().iterator().next();
519 assertThat(directory.getReportAttributes().getVersion()).isEqualTo("v2");
521 Component file = directory.getChildren().iterator().next();
522 assertThat(file.getReportAttributes().getVersion()).isEqualTo("v3");
526 public void versions_of_module_directory_and_file_are_null_if_absent_from_report() {
527 ScannerReport.Component project = newBuilder()
532 scannerComponentProvider.add(newBuilder()
536 scannerComponentProvider.add(newBuilder()
541 scannerComponentProvider.add(newBuilder()
544 .setPath("src/js/Foo.js")
547 Component root = call(project);
549 Component module = root.getChildren().iterator().next();
550 assertThat(module.getReportAttributes().getVersion()).isNull();
552 Component directory = module.getChildren().iterator().next();
553 assertThat(directory.getReportAttributes().getVersion()).isNull();
555 Component file = directory.getChildren().iterator().next();
556 assertThat(file.getReportAttributes().getVersion()).isNull();
560 public void versions_of_module_directory_and_file_are_null_if_empty_in_report() {
561 ScannerReport.Component project = newBuilder()
566 scannerComponentProvider.add(newBuilder()
571 scannerComponentProvider.add(newBuilder()
577 scannerComponentProvider.add(newBuilder()
581 .setPath("src/js/Foo.js")
584 Component root = call(project);
586 Component module = root.getChildren().iterator().next();
587 assertThat(module.getReportAttributes().getVersion()).isNull();
589 Component directory = module.getChildren().iterator().next();
590 assertThat(directory.getReportAttributes().getVersion()).isNull();
592 Component file = directory.getChildren().iterator().next();
593 assertThat(file.getReportAttributes().getVersion()).isNull();
597 public void only_nb_of_lines_is_mandatory_on_file_attributes() {
598 ScannerReport.Component project = newBuilder()
603 scannerComponentProvider.add(newBuilder()
606 .setPath("src/js/Foo.js")
609 Component root = call(project);
610 Component file = root.getChildren().iterator().next();
611 assertThat(file.getFileAttributes().getLines()).isEqualTo(1);
612 assertThat(file.getFileAttributes().getLanguageKey()).isNull();
613 assertThat(file.getFileAttributes().isUnitTest()).isFalse();
617 public void language_file_attributes_is_null_if_empty_in_report() {
618 ScannerReport.Component project = newBuilder()
623 scannerComponentProvider.add(newBuilder()
626 .setPath("src/js/Foo.js")
630 Component root = call(project);
631 Component file = root.getChildren().iterator().next();
632 assertThat(file.getFileAttributes().getLanguageKey()).isNull();
636 public void file_attributes_are_fully_loaded_from_report() {
637 ScannerReport.Component project = newBuilder()
642 scannerComponentProvider.add(newBuilder()
645 .setPath("src/js/Foo.js")
650 Component root = call(project);
651 Component file = root.getChildren().iterator().next();
652 assertThat(file.getFileAttributes().getLines()).isEqualTo(1);
653 assertThat(file.getFileAttributes().getLanguageKey()).isEqualTo("js");
654 assertThat(file.getFileAttributes().isUnitTest()).isTrue();
658 public void throw_IAE_if_lines_is_absent_from_report() {
659 ScannerReport.Component project = newBuilder()
664 scannerComponentProvider.add(newBuilder()
667 .setPath("src/js/Foo.js"));
669 expectedException.expect(IllegalArgumentException.class);
670 expectedException.expectMessage("File 'src/js/Foo.js' has no line");
676 public void throw_IAE_if_lines_is_zero_in_report() {
677 ScannerReport.Component project = newBuilder()
682 scannerComponentProvider.add(newBuilder()
685 .setPath("src/js/Foo.js")
688 expectedException.expect(IllegalArgumentException.class);
689 expectedException.expectMessage("File 'src/js/Foo.js' has no line");
695 public void throw_IAE_if_lines_is_negative_in_report() {
696 ScannerReport.Component project = newBuilder()
701 scannerComponentProvider.add(newBuilder()
704 .setPath("src/js/Foo.js")
707 expectedException.expect(IllegalArgumentException.class);
708 expectedException.expectMessage("File 'src/js/Foo.js' has no line");
713 private static class ScannerComponentProvider extends ExternalResource implements Function<Integer, ScannerReport.Component> {
714 private final Map<Integer, ScannerReport.Component> components = new HashMap<>();
717 protected void before() throws Throwable {
722 public ScannerReport.Component apply(Integer componentRef) {
723 return Objects.requireNonNull(components.get(componentRef), "No Component for componentRef " + componentRef);
726 public ScannerReport.Component add(ScannerReport.Component.Builder builder) {
727 ScannerReport.Component component = builder.build();
728 ScannerReport.Component existing = components.put(component.getRef(), component);
729 checkArgument(existing == null, "Component %s already set for ref %s", existing, component.getRef());
734 private Component call(ScannerReport.Component project) {
735 return newUnderTest(null).buildProject(project);
738 private Component call(ScannerReport.Component project, @Nullable SnapshotDto baseAnalysis) {
739 return newUnderTest(baseAnalysis).buildProject(project);
742 private ComponentTreeBuilder newUnderTest(@Nullable SnapshotDto baseAnalysis) {
743 return new ComponentTreeBuilder(KEY_GENERATOR, PUBLIC_KEY_GENERATOR, UUID_SUPPLIER, scannerComponentProvider, projectInDb, baseAnalysis);
746 private static Map<Integer, Component> indexComponentByRef(Component root) {
747 Map<Integer, Component> componentsByRef = new HashMap<>();
748 new DepthTraversalTypeAwareCrawler(
749 new TypeAwareVisitorAdapter(CrawlerDepthLimit.FILE, PRE_ORDER) {
751 public void visitAny(Component any) {
752 componentsByRef.put(any.getReportAttributes().getRef(), any);
755 return componentsByRef;