3 * Copyright (C) 2009-2024 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.ce.task.projectanalysis.component;
22 import java.util.Arrays;
23 import java.util.EnumSet;
24 import java.util.HashMap;
25 import java.util.List;
27 import java.util.Objects;
28 import java.util.Optional;
29 import java.util.Random;
30 import java.util.function.Function;
31 import java.util.function.UnaryOperator;
32 import org.junit.jupiter.api.Test;
33 import org.junit.jupiter.api.extension.BeforeEachCallback;
34 import org.junit.jupiter.api.extension.ExtensionContext;
35 import org.junit.jupiter.api.extension.RegisterExtension;
36 import org.sonar.ce.task.projectanalysis.analysis.Branch;
37 import org.sonar.core.component.ComponentKeys;
38 import org.sonar.scanner.protocol.output.ScannerReport;
39 import org.sonar.server.project.Project;
41 import static com.google.common.base.Preconditions.checkArgument;
42 import static org.apache.commons.lang3.RandomStringUtils.randomAlphabetic;
43 import static org.assertj.core.api.Assertions.assertThat;
44 import static org.assertj.core.api.Assertions.assertThatThrownBy;
45 import static org.junit.Assert.fail;
46 import static org.mockito.Mockito.mock;
47 import static org.mockito.Mockito.when;
48 import static org.sonar.ce.task.projectanalysis.component.ComponentVisitor.Order.PRE_ORDER;
49 import static org.sonar.db.component.ComponentTesting.newPrivateProjectDto;
50 import static org.sonar.scanner.protocol.output.ScannerReport.Component.ComponentType.FILE;
51 import static org.sonar.scanner.protocol.output.ScannerReport.Component.ComponentType.PROJECT;
52 import static org.sonar.scanner.protocol.output.ScannerReport.Component.ComponentType.UNRECOGNIZED;
53 import static org.sonar.scanner.protocol.output.ScannerReport.Component.newBuilder;
55 class ComponentTreeBuilderTest {
57 private static final ComponentKeyGenerator KEY_GENERATOR = (projectKey, path) -> "generated_" + ComponentKeys.createEffectiveKey(projectKey, path);
58 private static final UnaryOperator<String> UUID_SUPPLIER = (componentKey) -> componentKey + "_uuid";
59 private static final EnumSet<ScannerReport.Component.ComponentType> REPORT_TYPES = EnumSet.of(PROJECT, FILE);
60 private static final String NO_SCM_BASE_PATH = "";
61 // both no project as "" or null should be supported
62 private static final ProjectAttributes SOME_PROJECT_ATTRIBUTES = new ProjectAttributes(
63 randomAlphabetic(20), new Random().nextBoolean() ? null : randomAlphabetic(12), "1def5123");
66 private final ScannerComponentProvider scannerComponentProvider = new ScannerComponentProvider();
68 private final Project projectInDb = Project.from(newPrivateProjectDto(UUID_SUPPLIER.apply("K1")).setKey("K1").setDescription(null));
71 void build_throws_IAE_for_all_types_except_PROJECT_and_FILE() {
72 Arrays.stream(ScannerReport.Component.ComponentType.values())
73 .filter((type) -> type != UNRECOGNIZED)
74 .filter((type) -> !REPORT_TYPES.contains(type))
77 scannerComponentProvider.clear();
78 ScannerReport.Component project = newBuilder()
80 .setKey(projectInDb.getKey())
83 .setProjectRelativePath("root")
85 scannerComponentProvider.add(newBuilder()
88 .setProjectRelativePath("src")
91 call(project, NO_SCM_BASE_PATH, SOME_PROJECT_ATTRIBUTES);
92 fail("Should have thrown a IllegalArgumentException");
93 } catch (IllegalArgumentException e) {
94 assertThat(e).hasMessage("Unsupported component type '" + type + "'");
100 void build_throws_IAE_if_root_is_not_PROJECT() {
101 Arrays.stream(ScannerReport.Component.ComponentType.values())
102 .filter((type) -> type != UNRECOGNIZED)
103 .filter((type) -> !REPORT_TYPES.contains(type))
106 ScannerReport.Component component = newBuilder().setType(type).build();
109 fail("Should have thrown a IllegalArgumentException");
110 } catch (IllegalArgumentException e) {
111 assertThat(e).hasMessage("Expected root component of type 'PROJECT'");
117 void by_default_project_fields_are_loaded_from_report() {
118 String nameInReport = "the name";
119 String descriptionInReport = "the desc";
120 String buildString = randomAlphabetic(21);
121 Component root = call(newBuilder()
123 .setKey(projectInDb.getKey())
125 .setName(nameInReport)
126 .setDescription(descriptionInReport)
127 .build(), NO_SCM_BASE_PATH, new ProjectAttributes("6.5", buildString, "4124af4"));
129 assertThat(root.getUuid()).isEqualTo("generated_K1_uuid");
130 assertThat(root.getKey()).isEqualTo("generated_K1");
131 assertThat(root.getType()).isEqualTo(Component.Type.PROJECT);
132 assertThat(root.getName()).isEqualTo(nameInReport);
133 assertThat(root.getShortName()).isEqualTo(nameInReport);
134 assertThat(root.getDescription()).isEqualTo(descriptionInReport);
135 assertThat(root.getReportAttributes().getRef()).isEqualTo(42);
136 assertThat(root.getProjectAttributes().getProjectVersion()).contains("6.5");
137 assertThat(root.getProjectAttributes().getBuildString()).isEqualTo(Optional.of(buildString));
138 assertThatFileAttributesAreNotSet(root);
142 void project_name_is_loaded_from_db_if_absent_from_report() {
143 Component root = call(newBuilder()
145 .build(), NO_SCM_BASE_PATH, SOME_PROJECT_ATTRIBUTES);
147 assertThat(root.getName()).isEqualTo(projectInDb.getName());
151 void project_name_is_loaded_from_report_if_present_and_on_main_branch() {
152 String reportName = randomAlphabetic(5);
153 ScannerReport.Component reportProject = newBuilder()
158 Component root = newUnderTest(SOME_PROJECT_ATTRIBUTES, true).buildProject(reportProject, NO_SCM_BASE_PATH);
160 assertThat(root.getName()).isEqualTo(reportName);
164 void project_name_is_loaded_from_db_if_not_on_main_branch() {
165 String reportName = randomAlphabetic(5);
166 ScannerReport.Component reportProject = newBuilder()
171 Component root = newUnderTest(SOME_PROJECT_ATTRIBUTES, false)
172 .buildProject(reportProject, NO_SCM_BASE_PATH);
174 assertThat(root.getName()).isEqualTo(projectInDb.getName());
178 void project_description_is_loaded_from_db_if_absent_from_report() {
179 Component root = call(newBuilder()
181 .build(), NO_SCM_BASE_PATH, SOME_PROJECT_ATTRIBUTES);
183 assertThat(root.getDescription()).isEqualTo(projectInDb.getDescription());
187 void project_description_is_loaded_from_report_if_present_and_on_main_branch() {
188 String reportDescription = randomAlphabetic(5);
189 ScannerReport.Component reportProject = newBuilder()
191 .setDescription(reportDescription)
194 Component root = newUnderTest(SOME_PROJECT_ATTRIBUTES, true).buildProject(reportProject, NO_SCM_BASE_PATH);
196 assertThat(root.getDescription()).isEqualTo(reportDescription);
200 void project_description_is_loaded_from_db_if_not_on_main_branch() {
201 String reportDescription = randomAlphabetic(5);
202 ScannerReport.Component reportProject = newBuilder()
204 .setDescription(reportDescription)
207 Component root = newUnderTest(SOME_PROJECT_ATTRIBUTES, false).buildProject(reportProject, NO_SCM_BASE_PATH);
209 assertThat(root.getDescription()).isEqualTo(projectInDb.getDescription());
213 void project_scmPath_is_empty_if_scmBasePath_is_empty() {
214 Component root = call(newBuilder()
216 .build(), NO_SCM_BASE_PATH, SOME_PROJECT_ATTRIBUTES);
218 assertThat(root.getReportAttributes().getScmPath()).isEmpty();
222 void projectAttributes_is_constructor_argument() {
223 Component root = call(newBuilder()
225 .build(), NO_SCM_BASE_PATH, SOME_PROJECT_ATTRIBUTES);
227 assertThat(root.getProjectAttributes()).isSameAs(SOME_PROJECT_ATTRIBUTES);
231 void any_component_with_projectRelativePath_has_this_value_as_scmPath_if_scmBasePath_is_empty() {
232 ScannerReport.Component project = newBuilder()
234 .setKey(projectInDb.getKey())
237 .setProjectRelativePath("root")
239 scannerComponentProvider.add(newBuilder()
242 .setProjectRelativePath("src/js/Foo.js")
245 Component root = call(project, NO_SCM_BASE_PATH, SOME_PROJECT_ATTRIBUTES);
247 assertThat(root.getReportAttributes().getScmPath())
249 Component directory = root.getChildren().iterator().next();
250 assertThat(directory.getReportAttributes().getScmPath())
252 Component file = directory.getChildren().iterator().next();
253 assertThat(file.getReportAttributes().getScmPath())
254 .contains("src/js/Foo.js");
258 void any_component_with_projectRelativePath_has_this_value_appended_to_scmBasePath_and_a_slash_as_scmPath_if_scmBasePath_is_not_empty() {
259 ScannerReport.Component project = createProject();
260 String scmBasePath = randomAlphabetic(10);
262 Component root = call(project, scmBasePath, SOME_PROJECT_ATTRIBUTES);
263 assertThat(root.getReportAttributes().getScmPath())
264 .contains(scmBasePath);
265 Component directory = root.getChildren().iterator().next();
266 assertThat(directory.getReportAttributes().getScmPath())
267 .contains(scmBasePath + "/src/js");
268 Component file = directory.getChildren().iterator().next();
269 assertThat(file.getReportAttributes().getScmPath())
270 .contains(scmBasePath + "/src/js/Foo.js");
273 private ScannerReport.Component createProject() {
274 ScannerReport.Component project = newBuilder()
276 .setKey(projectInDb.getKey())
280 scannerComponentProvider.add(newBuilder()
283 .setProjectRelativePath("src/js/Foo.js")
289 void keys_of_directory_and_file_are_generated() {
290 ScannerReport.Component project = createProject();
292 Component root = call(project);
293 assertThat(root.getKey()).isEqualTo("generated_" + projectInDb.getKey());
294 assertThat(root.getChildren()).hasSize(1);
296 Component directory = root.getChildren().iterator().next();
297 assertThat(directory.getKey()).isEqualTo("generated_" + projectInDb.getKey() + ":src/js");
298 assertThat(directory.getChildren()).hasSize(1);
300 Component file = directory.getChildren().iterator().next();
301 assertThat(file.getKey()).isEqualTo("generated_" + projectInDb.getKey() + ":src/js/Foo.js");
302 assertThat(file.getChildren()).isEmpty();
306 void modules_are_not_created() {
307 ScannerReport.Component project = newBuilder()
309 .setKey(projectInDb.getKey())
313 scannerComponentProvider.add(newBuilder()
316 .setProjectRelativePath("src/js/Foo.js")
319 Component root = call(project);
321 List<Component> components = root.getChildren();
322 assertThat(components).extracting("type").containsOnly(Component.Type.DIRECTORY);
326 void folder_hierarchy_is_created() {
327 ScannerReport.Component project = newBuilder()
329 .setKey(projectInDb.getKey())
335 scannerComponentProvider.add(newBuilder()
338 .setProjectRelativePath("src/main/xoo/Foo1.js")
340 scannerComponentProvider.add(newBuilder()
343 .setProjectRelativePath("src/test/xoo/org/sonar/Foo2.js")
345 scannerComponentProvider.add(newBuilder()
348 .setProjectRelativePath("pom.xml")
351 Component root = call(project);
352 assertThat(root.getChildren()).hasSize(2);
354 Component pom = root.getChildren().get(1);
355 assertThat(pom.getKey()).isEqualTo("generated_K1:pom.xml");
356 assertThat(pom.getName()).isEqualTo("pom.xml");
358 Component directory = root.getChildren().get(0);
359 assertThat(directory.getKey()).isEqualTo("generated_K1:src");
360 assertThat(directory.getName()).isEqualTo("src");
362 // folders are collapsed and they only contain one directory
363 Component d1 = directory.getChildren().get(0);
364 assertThat(d1.getKey()).isEqualTo("generated_K1:src/main/xoo");
365 assertThat(d1.getName()).isEqualTo("src/main/xoo");
366 assertThat(d1.getShortName()).isEqualTo("main/xoo");
368 Component d2 = directory.getChildren().get(1);
369 assertThat(d2.getKey()).isEqualTo("generated_K1:src/test/xoo/org/sonar");
370 assertThat(d2.getName()).isEqualTo("src/test/xoo/org/sonar");
371 assertThat(d2.getShortName()).isEqualTo("test/xoo/org/sonar");
375 void collapse_directories_from_root() {
376 ScannerReport.Component project = newBuilder()
378 .setKey(projectInDb.getKey())
382 scannerComponentProvider.add(newBuilder()
385 .setProjectRelativePath("src/test/xoo/org/sonar/Foo2.js")
388 Component root = call(project);
390 // folders are collapsed and they only contain one directory
391 Component dir = root.getChildren().get(0);
392 assertThat(dir.getKey()).isEqualTo("generated_K1:src/test/xoo/org/sonar");
393 assertThat(dir.getName()).isEqualTo("src/test/xoo/org/sonar");
394 assertThat(dir.getShortName()).isEqualTo("src/test/xoo/org/sonar");
396 Component file = dir.getChildren().get(0);
397 assertThat(file.getKey()).isEqualTo("generated_K1:src/test/xoo/org/sonar/Foo2.js");
398 assertThat(file.getName()).isEqualTo("src/test/xoo/org/sonar/Foo2.js");
399 assertThat(file.getShortName()).isEqualTo("Foo2.js");
403 void directories_are_collapsed() {
404 ScannerReport.Component project = newBuilder()
406 .setKey(projectInDb.getKey())
410 scannerComponentProvider.add(newBuilder()
413 .setProjectRelativePath("src/js/Foo.js")
416 Component root = call(project);
418 Component directory = root.getChildren().iterator().next();
419 assertThat(directory.getKey()).isEqualTo("generated_K1:src/js");
420 assertThat(directory.getName()).isEqualTo("src/js");
421 assertThat(directory.getShortName()).isEqualTo("src/js");
423 Component file = directory.getChildren().iterator().next();
424 assertThat(file.getKey()).isEqualTo("generated_K1:src/js/Foo.js");
425 assertThat(file.getName()).isEqualTo("src/js/Foo.js");
426 assertThat(file.getShortName()).isEqualTo("Foo.js");
430 void names_of_directory_and_file_are_based_on_the_path() {
431 ScannerReport.Component project = newBuilder()
433 .setKey(projectInDb.getKey())
437 scannerComponentProvider.add(newBuilder()
440 .setProjectRelativePath("src/js/Foo.js")
444 Component root = call(project);
446 Component directory = root.getChildren().iterator().next();
447 assertThat(directory.getName()).isEqualTo("src/js");
448 assertThat(directory.getShortName()).isEqualTo("src/js");
450 Component file = directory.getChildren().iterator().next();
451 assertThat(file.getName()).isEqualTo("src/js/Foo.js");
452 assertThat(file.getShortName()).isEqualTo("Foo.js");
456 void create_full_hierarchy_of_directories() {
457 ScannerReport.Component project = newBuilder()
459 .setKey(projectInDb.getKey())
464 scannerComponentProvider.add(newBuilder()
467 .setProjectRelativePath("src/java/Bar.java")
470 scannerComponentProvider.add(newBuilder()
473 .setProjectRelativePath("src/js/Foo.js")
477 Component root = call(project);
479 Component directory = root.getChildren().iterator().next();
480 assertThat(directory.getKey()).isEqualTo("generated_K1:src");
481 assertThat(directory.getName()).isEqualTo("src");
482 assertThat(directory.getShortName()).isEqualTo("src");
484 Component directoryJava = directory.getChildren().get(0);
485 assertThat(directoryJava.getKey()).isEqualTo("generated_K1:src/java");
486 assertThat(directoryJava.getName()).isEqualTo("src/java");
487 assertThat(directoryJava.getShortName()).isEqualTo("java");
489 Component directoryJs = directory.getChildren().get(1);
490 assertThat(directoryJs.getKey()).isEqualTo("generated_K1:src/js");
491 assertThat(directoryJs.getName()).isEqualTo("src/js");
492 assertThat(directoryJs.getShortName()).isEqualTo("js");
494 Component file = directoryJs.getChildren().iterator().next();
495 assertThat(file.getKey()).isEqualTo("generated_K1:src/js/Foo.js");
496 assertThat(file.getName()).isEqualTo("src/js/Foo.js");
497 assertThat(file.getShortName()).isEqualTo("Foo.js");
500 private void assertThatFileAttributesAreNotSet(Component root) {
502 root.getFileAttributes();
504 } catch (IllegalStateException e) {
505 assertThat(e).hasMessage("Only component of type FILE have a FileAttributes object");
510 void keys_of_directory_and_files_includes_always_root_project() {
511 ScannerReport.Component project = newBuilder()
515 .addChildRef(31).build();
516 scannerComponentProvider.add(newBuilder().setRef(31).setType(FILE).setProjectRelativePath("file in project").setLines(1));
517 Component root = call(project);
518 Map<String, Component> componentsByKey = indexComponentByKey(root);
520 assertThat(componentsByKey.values()).extracting("key").startsWith("generated_project 1");
524 void uuids_are_provided_by_supplier() {
525 ScannerReport.Component project = newBuilder()
531 scannerComponentProvider.add(newBuilder()
534 .setProjectRelativePath("src/js/Foo.js")
537 Component root = call(project);
538 assertThat(root.getUuid()).isEqualTo("generated_c1_uuid");
540 Component directory = root.getChildren().iterator().next();
541 assertThat(directory.getUuid()).isEqualTo("generated_c1:src/js_uuid");
543 Component file = directory.getChildren().iterator().next();
544 assertThat(file.getUuid()).isEqualTo("generated_c1:src/js/Foo.js_uuid");
548 void files_have_markedAsUnchanged_flag() {
549 ScannerReport.Component project = newBuilder()
555 scannerComponentProvider.add(newBuilder()
558 .setMarkedAsUnchanged(true)
559 .setProjectRelativePath("src/js/Foo.js")
562 Component root = call(project);
563 assertThat(root.getUuid()).isEqualTo("generated_c1_uuid");
565 Component directory = root.getChildren().iterator().next();
566 assertThat(directory.getUuid()).isEqualTo("generated_c1:src/js_uuid");
568 Component file = directory.getChildren().iterator().next();
569 assertThat(file.getFileAttributes().isMarkedAsUnchanged()).isTrue();
573 void issues_are_relocated_from_directories_and_modules_to_root() {
574 ScannerReport.Component project = newBuilder()
580 ScannerReport.Component.Builder file = newBuilder()
583 .setProjectRelativePath("src/js/Foo.js")
585 scannerComponentProvider.add(file);
591 void descriptions_of_module_directory_and_file_are_null_if_absent_from_report() {
592 ScannerReport.Component project = newBuilder()
597 scannerComponentProvider.add(newBuilder()
600 .setProjectRelativePath("src/js/Foo.js")
603 Component root = call(project);
605 Component directory = root.getChildren().iterator().next();
606 assertThat(directory.getDescription()).isNull();
608 Component file = directory.getChildren().iterator().next();
609 assertThat(file.getDescription()).isNull();
613 void descriptions_of_module_directory_and_file_are_null_if_empty_in_report() {
614 ScannerReport.Component project = newBuilder()
620 scannerComponentProvider.add(newBuilder()
624 .setProjectRelativePath("src/js/Foo.js")
627 Component root = call(project);
629 Component directory = root.getChildren().iterator().next();
630 assertThat(directory.getDescription()).isNull();
632 Component file = directory.getChildren().iterator().next();
633 assertThat(file.getDescription()).isNull();
637 void descriptions_of_module_directory_and_file_are_set_from_report_if_present() {
638 ScannerReport.Component project = newBuilder()
643 scannerComponentProvider.add(newBuilder()
647 .setProjectRelativePath("src/js/Foo.js")
650 Component root = call(project);
651 Component directory = root.getChildren().iterator().next();
652 assertThat(directory.getDescription()).isNull();
654 Component file = directory.getChildren().iterator().next();
655 assertThat(file.getDescription()).isEqualTo("d");
659 void only_nb_of_lines_is_mandatory_on_file_attributes() {
660 ScannerReport.Component project = newBuilder()
665 scannerComponentProvider.add(newBuilder()
668 .setProjectRelativePath("src/js/Foo.js")
671 Component root = call(project);
672 Component dir = root.getChildren().iterator().next();
673 Component file = dir.getChildren().iterator().next();
674 assertThat(file.getFileAttributes().getLines()).isOne();
675 assertThat(file.getFileAttributes().getLanguageKey()).isNull();
676 assertThat(file.getFileAttributes().isUnitTest()).isFalse();
680 void language_file_attributes_is_null_if_empty_in_report() {
681 ScannerReport.Component project = newBuilder()
686 scannerComponentProvider.add(newBuilder()
689 .setProjectRelativePath("src/js/Foo.js")
693 Component root = call(project);
694 Component dir2 = root.getChildren().iterator().next();
696 Component file = dir2.getChildren().iterator().next();
697 assertThat(file.getFileAttributes().getLanguageKey()).isNull();
701 void file_attributes_are_fully_loaded_from_report() {
702 ScannerReport.Component project = newBuilder()
707 scannerComponentProvider.add(newBuilder()
710 .setProjectRelativePath("src/js/Foo.js")
715 Component root = call(project);
716 Component dir = root.getChildren().iterator().next();
717 Component file = dir.getChildren().iterator().next();
718 assertThat(file.getFileAttributes().getLines()).isOne();
719 assertThat(file.getFileAttributes().getLanguageKey()).isEqualTo("js");
720 assertThat(file.getFileAttributes().isUnitTest()).isTrue();
724 void throw_IAE_if_lines_is_absent_from_report() {
725 ScannerReport.Component project = newBuilder()
730 scannerComponentProvider.add(newBuilder()
733 .setProjectRelativePath("src/js/Foo.js"));
735 assertThatThrownBy(() -> call(project))
736 .isInstanceOf(IllegalArgumentException.class)
737 .hasMessage("File 'src/js/Foo.js' has no line");
741 void throw_IAE_if_lines_is_zero_in_report() {
742 ScannerReport.Component project = newBuilder()
747 scannerComponentProvider.add(newBuilder()
750 .setProjectRelativePath("src/js/Foo.js")
753 assertThatThrownBy(() -> call(project))
754 .isInstanceOf(IllegalArgumentException.class)
755 .hasMessage("File 'src/js/Foo.js' has no line");
759 void throw_IAE_if_lines_is_negative_in_report() {
760 ScannerReport.Component project = newBuilder()
765 scannerComponentProvider.add(newBuilder()
768 .setProjectRelativePath("src/js/Foo.js")
771 assertThatThrownBy(() -> call(project))
772 .isInstanceOf(IllegalArgumentException.class)
773 .hasMessage("File 'src/js/Foo.js' has no line");
776 private static class ScannerComponentProvider implements Function<Integer, ScannerReport.Component>, BeforeEachCallback {
777 private final Map<Integer, ScannerReport.Component> components = new HashMap<>();
779 public void clear() {
784 public ScannerReport.Component apply(Integer componentRef) {
785 return Objects.requireNonNull(components.get(componentRef), "No Component for componentRef " + componentRef);
788 public ScannerReport.Component add(ScannerReport.Component.Builder builder) {
789 ScannerReport.Component component = builder.build();
790 ScannerReport.Component existing = components.put(component.getRef(), component);
791 checkArgument(existing == null, "Component %s already set for ref %s", existing, component.getRef());
796 public void beforeEach(ExtensionContext extensionContext) {
801 private Component call(ScannerReport.Component project) {
802 return call(project, NO_SCM_BASE_PATH, SOME_PROJECT_ATTRIBUTES);
805 private Component call(ScannerReport.Component project, String scmBasePath, ProjectAttributes projectAttributes) {
806 return newUnderTest(projectAttributes, true).buildProject(project, scmBasePath);
809 private ComponentTreeBuilder newUnderTest(ProjectAttributes projectAttributes, boolean mainBranch) {
810 Branch branch = mock(Branch.class);
811 when(branch.isMain()).thenReturn(mainBranch);
812 return new ComponentTreeBuilder(KEY_GENERATOR, UUID_SUPPLIER, scannerComponentProvider, projectInDb, branch, projectAttributes);
815 private static Map<String, Component> indexComponentByKey(Component root) {
816 Map<String, Component> componentsByKey = new HashMap<>();
817 new DepthTraversalTypeAwareCrawler(
818 new TypeAwareVisitorAdapter(CrawlerDepthLimit.FILE, PRE_ORDER) {
820 public void visitAny(Component any) {
821 componentsByKey.put(any.getKey(), any);
824 return componentsByKey;