]> source.dussan.org Git - sonarqube.git/blob
6697a44b2666d3fc99afc49de9919efe50a32e96
[sonarqube.git] /
1 /*
2  * SonarQube
3  * Copyright (C) 2009-2017 SonarSource SA
4  * mailto:info AT sonarsource DOT com
5  *
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.
10  *
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.
15  *
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.
19  */
20 package org.sonar.server.computation.task.projectanalysis.component;
21
22 import java.util.Arrays;
23 import java.util.EnumSet;
24 import java.util.HashMap;
25 import java.util.Map;
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;
37
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;
48
49 public class ComponentTreeBuilderTest {
50
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);
55
56   @Rule
57   public ExpectedException expectedException = ExpectedException.none();
58
59   @Rule
60   public ScannerComponentProvider scannerComponentProvider = new ScannerComponentProvider();
61
62   private Project projectInDb = new Project(UUID_SUPPLIER.apply("K1"), "K1", "theProjectName");
63
64   @Test
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))
69       .forEach(
70         (type) -> {
71           ScannerReport.Component component = newBuilder().setType(type).build();
72           try {
73             call(component);
74             fail("Should have thrown a IllegalArgumentException");
75           } catch (IllegalArgumentException e) {
76             assertThat(e).hasMessage("Unsupported component type '" + type + "'");
77           }
78         });
79   }
80
81   @Test
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()
86       .setType(PROJECT)
87       .setKey(projectInDb.getKey())
88       .setRef(42)
89       .setName(nameInReport)
90       .setDescription(descriptionInReport)
91       .setVersion("6.5")
92       .build());
93
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);
103   }
104
105   @Test
106   public void project_name_is_loaded_from_db_if_absent_from_report() {
107     Component root = call(newBuilder()
108       .setType(PROJECT)
109       .build());
110
111     assertThat(root.getName()).isEqualTo(projectInDb.getName());
112   }
113
114   @Test
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()
118       .setType(PROJECT)
119       .build(), baseAnalysis);
120
121     assertThat(root.getReportAttributes().getVersion()).isEqualTo("6.5");
122   }
123
124   @Test
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()
128       .setType(PROJECT)
129       .setVersion("")
130       .build(), baseAnalysis);
131
132     assertThat(root.getReportAttributes().getVersion()).isEqualTo("6.5");
133   }
134
135   @Test
136   public void project_version_is_hardcoded_if_absent_from_report_and_db() {
137     Component root = call(newBuilder()
138       .setType(PROJECT)
139       .build());
140
141     assertThat(root.getReportAttributes().getVersion()).isEqualTo("not provided");
142   }
143
144   @Test
145   public void project_description_is_null_if_absent_from_report() {
146     Component root = call(newBuilder()
147       .setType(PROJECT)
148       .build());
149
150     assertThat(root.getDescription()).isNull();
151   }
152
153   @Test
154   public void keys_of_module_directory_and_file_are_generated() {
155     ScannerReport.Component project = newBuilder()
156       .setType(PROJECT)
157       .setKey(projectInDb.getKey())
158       .setRef(1)
159       .addChildRef(2)
160       .build();
161     scannerComponentProvider.add(newBuilder()
162       .setRef(2)
163       .setType(MODULE)
164       .setKey("M")
165       .addChildRef(3));
166     scannerComponentProvider.add(newBuilder()
167       .setRef(3)
168       .setType(DIRECTORY)
169       .setPath("src/js")
170       .addChildRef(4));
171     scannerComponentProvider.add(newBuilder()
172       .setRef(4)
173       .setType(FILE)
174       .setPath("src/js/Foo.js")
175       .setLines(1));
176
177     Component root = call(project);
178     assertThat(root.getKey()).isEqualTo("generated_" + projectInDb.getKey());
179     assertThat(root.getChildren()).hasSize(1);
180
181     Component module = root.getChildren().iterator().next();
182     assertThat(module.getKey()).isEqualTo("generated_M");
183     assertThat(module.getChildren()).hasSize(1);
184
185     Component directory = module.getChildren().iterator().next();
186     assertThat(directory.getKey()).isEqualTo("generated_M:src/js");
187     assertThat(directory.getChildren()).hasSize(1);
188
189     Component file = directory.getChildren().iterator().next();
190     assertThat(file.getKey()).isEqualTo("generated_M:src/js/Foo.js");
191     assertThat(file.getChildren()).isEmpty();
192   }
193
194   @Test
195   public void names_of_module_directory_and_file_are_keys_if_names_are_absent_from_report() {
196     ScannerReport.Component project = newBuilder()
197       .setType(PROJECT)
198       .setKey(projectInDb.getKey())
199       .setRef(1)
200       .addChildRef(2)
201       .build();
202     scannerComponentProvider.add(newBuilder()
203       .setRef(2)
204       .setType(MODULE)
205       .setKey("M")
206       .addChildRef(3));
207     scannerComponentProvider.add(newBuilder()
208       .setRef(3)
209       .setType(DIRECTORY)
210       .setPath("src/js")
211       .addChildRef(4));
212     scannerComponentProvider.add(newBuilder()
213       .setRef(4)
214       .setType(FILE)
215       .setPath("src/js/Foo.js")
216       .setLines(1));
217
218     Component root = call(project);
219
220     Component module = root.getChildren().iterator().next();
221     assertThat(module.getName()).isEqualTo("generated_M");
222
223     Component directory = module.getChildren().iterator().next();
224     assertThat(directory.getName()).isEqualTo("generated_M:src/js");
225
226     Component file = directory.getChildren().iterator().next();
227     assertThat(file.getName()).isEqualTo("generated_M:src/js/Foo.js");
228   }
229
230   @Test
231   public void names_of_module_directory_and_file_are_keys_if_names_are_empty_in_report() {
232     ScannerReport.Component project = newBuilder()
233       .setType(PROJECT)
234       .setKey(projectInDb.getKey())
235       .setRef(1)
236       .addChildRef(2)
237       .build();
238     scannerComponentProvider.add(newBuilder()
239       .setRef(2)
240       .setType(MODULE)
241       .setKey("M")
242       .setName("")
243       .addChildRef(3));
244     scannerComponentProvider.add(newBuilder()
245       .setRef(3)
246       .setType(DIRECTORY)
247       .setPath("src/js")
248       .setName("")
249       .addChildRef(4));
250     scannerComponentProvider.add(newBuilder()
251       .setRef(4)
252       .setType(FILE)
253       .setPath("src/js/Foo.js")
254       .setName("")
255       .setLines(1));
256
257     Component root = call(project);
258
259     Component module = root.getChildren().iterator().next();
260     assertThat(module.getName()).isEqualTo("generated_M");
261
262     Component directory = module.getChildren().iterator().next();
263     assertThat(directory.getName()).isEqualTo("generated_M:src/js");
264
265     Component file = directory.getChildren().iterator().next();
266     assertThat(file.getName()).isEqualTo("generated_M:src/js/Foo.js");
267   }
268
269   private void assertThatFileAttributesAreNotSet(Component root) {
270     try {
271       root.getFileAttributes();
272       fail();
273     } catch (IllegalStateException e) {
274       assertThat(e).hasMessage("Only component of type FILE have a FileAttributes object");
275     }
276   }
277
278   @Test
279   public void name_of_module_directory_and_files_includes_name_of_closest_module() {
280     ScannerReport.Component project = newBuilder()
281       .setType(PROJECT)
282       .setKey("project 1")
283       .setRef(1)
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));
300
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:");
318   }
319
320   @Test
321   public void uuids_are_provided_by_supplier() {
322     ScannerReport.Component project = newBuilder()
323       .setType(PROJECT)
324       .setKey("c1")
325       .setRef(1)
326       .addChildRef(2)
327       .build();
328     scannerComponentProvider.add(newBuilder()
329       .setRef(2)
330       .setType(MODULE)
331       .setKey("c2")
332       .addChildRef(3));
333     scannerComponentProvider.add(newBuilder()
334       .setRef(3)
335       .setType(DIRECTORY)
336       .setPath("src/js")
337       .addChildRef(4));
338     scannerComponentProvider.add(newBuilder()
339       .setRef(4)
340       .setType(FILE)
341       .setPath("src/js/Foo.js")
342       .setLines(1));
343
344     Component root = call(project);
345     assertThat(root.getUuid()).isEqualTo("generated_c1_uuid");
346
347     Component module = root.getChildren().iterator().next();
348     assertThat(module.getUuid()).isEqualTo("generated_c2_uuid");
349
350     Component directory = module.getChildren().iterator().next();
351     assertThat(directory.getUuid()).isEqualTo("generated_c2:src/js_uuid");
352
353     Component file = directory.getChildren().iterator().next();
354     assertThat(file.getUuid()).isEqualTo("generated_c2:src/js/Foo.js_uuid");
355   }
356
357   @Test
358   public void descriptions_of_module_directory_and_file_are_null_if_absent_from_report() {
359     ScannerReport.Component project = newBuilder()
360       .setType(PROJECT)
361       .setRef(1)
362       .addChildRef(2)
363       .build();
364     scannerComponentProvider.add(newBuilder()
365       .setRef(2)
366       .setType(MODULE)
367       .addChildRef(3));
368     scannerComponentProvider.add(newBuilder()
369       .setRef(3)
370       .setType(DIRECTORY)
371       .setPath("src/js")
372       .addChildRef(4));
373     scannerComponentProvider.add(newBuilder()
374       .setRef(4)
375       .setType(FILE)
376       .setPath("src/js/Foo.js")
377       .setLines(1));
378
379     Component root = call(project);
380
381     Component module = root.getChildren().iterator().next();
382     assertThat(module.getDescription()).isNull();
383
384     Component directory = module.getChildren().iterator().next();
385     assertThat(directory.getDescription()).isNull();
386
387     Component file = directory.getChildren().iterator().next();
388     assertThat(file.getDescription()).isNull();
389   }
390
391   @Test
392   public void descriptions_of_module_directory_and_file_are_null_if_empty_in_report() {
393     ScannerReport.Component project = newBuilder()
394       .setType(PROJECT)
395       .setRef(1)
396       .setDescription("")
397       .addChildRef(2)
398       .build();
399     scannerComponentProvider.add(newBuilder()
400       .setRef(2)
401       .setType(MODULE)
402       .setDescription("")
403       .addChildRef(3));
404     scannerComponentProvider.add(newBuilder()
405       .setRef(3)
406       .setType(DIRECTORY)
407       .setDescription("")
408       .setPath("src/js")
409       .addChildRef(4));
410     scannerComponentProvider.add(newBuilder()
411       .setRef(4)
412       .setType(FILE)
413       .setDescription("")
414       .setPath("src/js/Foo.js")
415       .setLines(1));
416
417     Component root = call(project);
418
419     Component module = root.getChildren().iterator().next();
420     assertThat(module.getDescription()).isNull();
421
422     Component directory = module.getChildren().iterator().next();
423     assertThat(directory.getDescription()).isNull();
424
425     Component file = directory.getChildren().iterator().next();
426     assertThat(file.getDescription()).isNull();
427   }
428
429   @Test
430   public void descriptions_of_module_directory_and_file_are_set_from_report_if_present() {
431     ScannerReport.Component project = newBuilder()
432       .setType(PROJECT)
433       .setRef(1)
434       .addChildRef(2)
435       .build();
436     scannerComponentProvider.add(newBuilder()
437       .setRef(2)
438       .setType(MODULE)
439       .setDescription("b")
440       .addChildRef(3));
441     scannerComponentProvider.add(newBuilder()
442       .setRef(3)
443       .setType(DIRECTORY)
444       .setDescription("c")
445       .setPath("src/js")
446       .addChildRef(4));
447     scannerComponentProvider.add(newBuilder()
448       .setRef(4)
449       .setType(FILE)
450       .setDescription("d")
451       .setPath("src/js/Foo.js")
452       .setLines(1));
453
454     Component root = call(project);
455
456     Component module = root.getChildren().iterator().next();
457     assertThat(module.getDescription()).isEqualTo("b");
458
459     Component directory = module.getChildren().iterator().next();
460     assertThat(directory.getDescription()).isEqualTo("c");
461
462     Component file = directory.getChildren().iterator().next();
463     assertThat(file.getDescription()).isEqualTo("d");
464   }
465
466   @Test
467   public void versions_of_module_directory_and_file_are_set_from_report_if_present() {
468     ScannerReport.Component project = newBuilder()
469       .setType(PROJECT)
470       .setRef(1)
471       .addChildRef(2)
472       .build();
473     scannerComponentProvider.add(newBuilder()
474       .setRef(2)
475       .setType(MODULE)
476       .setVersion("v1")
477       .addChildRef(3));
478     scannerComponentProvider.add(newBuilder()
479       .setRef(3)
480       .setType(DIRECTORY)
481       .setVersion("v2")
482       .setPath("src/js")
483       .addChildRef(4));
484     scannerComponentProvider.add(newBuilder()
485       .setRef(4)
486       .setType(FILE)
487       .setVersion("v3")
488       .setPath("src/js/Foo.js")
489       .setLines(1));
490
491     Component root = call(project);
492
493     Component module = root.getChildren().iterator().next();
494     assertThat(module.getReportAttributes().getVersion()).isEqualTo("v1");
495
496     Component directory = module.getChildren().iterator().next();
497     assertThat(directory.getReportAttributes().getVersion()).isEqualTo("v2");
498
499     Component file = directory.getChildren().iterator().next();
500     assertThat(file.getReportAttributes().getVersion()).isEqualTo("v3");
501   }
502
503   @Test
504   public void versions_of_module_directory_and_file_are_null_if_absent_from_report() {
505     ScannerReport.Component project = newBuilder()
506       .setType(PROJECT)
507       .setRef(1)
508       .addChildRef(2)
509       .build();
510     scannerComponentProvider.add(newBuilder()
511       .setRef(2)
512       .setType(MODULE)
513       .addChildRef(3));
514     scannerComponentProvider.add(newBuilder()
515       .setRef(3)
516       .setType(DIRECTORY)
517       .setPath("src/js")
518       .addChildRef(4));
519     scannerComponentProvider.add(newBuilder()
520       .setRef(4)
521       .setType(FILE)
522       .setPath("src/js/Foo.js")
523       .setLines(1));
524
525     Component root = call(project);
526
527     Component module = root.getChildren().iterator().next();
528     assertThat(module.getReportAttributes().getVersion()).isNull();
529
530     Component directory = module.getChildren().iterator().next();
531     assertThat(directory.getReportAttributes().getVersion()).isNull();
532
533     Component file = directory.getChildren().iterator().next();
534     assertThat(file.getReportAttributes().getVersion()).isNull();
535   }
536
537   @Test
538   public void versions_of_module_directory_and_file_are_null_if_empty_in_report() {
539     ScannerReport.Component project = newBuilder()
540       .setType(PROJECT)
541       .setRef(1)
542       .addChildRef(2)
543       .build();
544     scannerComponentProvider.add(newBuilder()
545       .setRef(2)
546       .setType(MODULE)
547       .setVersion("")
548       .addChildRef(3));
549     scannerComponentProvider.add(newBuilder()
550       .setRef(3)
551       .setType(DIRECTORY)
552       .setVersion("")
553       .setPath("src/js")
554       .addChildRef(4));
555     scannerComponentProvider.add(newBuilder()
556       .setRef(4)
557       .setType(FILE)
558       .setVersion("")
559       .setPath("src/js/Foo.js")
560       .setLines(1));
561
562     Component root = call(project);
563
564     Component module = root.getChildren().iterator().next();
565     assertThat(module.getReportAttributes().getVersion()).isNull();
566
567     Component directory = module.getChildren().iterator().next();
568     assertThat(directory.getReportAttributes().getVersion()).isNull();
569
570     Component file = directory.getChildren().iterator().next();
571     assertThat(file.getReportAttributes().getVersion()).isNull();
572   }
573
574   @Test
575   public void only_nb_of_lines_is_mandatory_on_file_attributes() {
576     ScannerReport.Component project = newBuilder()
577       .setType(PROJECT)
578       .setRef(1)
579       .addChildRef(2)
580       .build();
581     scannerComponentProvider.add(newBuilder()
582       .setRef(2)
583       .setType(FILE)
584       .setPath("src/js/Foo.js")
585       .setLines(1));
586
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();
592   }
593
594   @Test
595   public void language_file_attributes_is_null_if_empty_in_report() {
596     ScannerReport.Component project = newBuilder()
597       .setType(PROJECT)
598       .setRef(1)
599       .addChildRef(2)
600       .build();
601     scannerComponentProvider.add(newBuilder()
602       .setRef(2)
603       .setType(FILE)
604       .setPath("src/js/Foo.js")
605       .setLines(1)
606       .setLanguage(""));
607
608     Component root = call(project);
609     Component file = root.getChildren().iterator().next();
610     assertThat(file.getFileAttributes().getLanguageKey()).isNull();
611   }
612
613   @Test
614   public void file_attributes_are_fully_loaded_from_report() {
615     ScannerReport.Component project = newBuilder()
616       .setType(PROJECT)
617       .setRef(1)
618       .addChildRef(2)
619       .build();
620     scannerComponentProvider.add(newBuilder()
621       .setRef(2)
622       .setType(FILE)
623       .setPath("src/js/Foo.js")
624       .setLines(1)
625       .setLanguage("js")
626       .setIsTest(true));
627
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();
633   }
634
635   @Test
636   public void throw_IAE_if_lines_is_absent_from_report() {
637     ScannerReport.Component project = newBuilder()
638       .setType(PROJECT)
639       .setRef(1)
640       .addChildRef(2)
641       .build();
642     scannerComponentProvider.add(newBuilder()
643       .setRef(2)
644       .setType(FILE)
645       .setPath("src/js/Foo.js"));
646
647     expectedException.expect(IllegalArgumentException.class);
648     expectedException.expectMessage("File 'src/js/Foo.js' has no line");
649
650     call(project);
651   }
652
653   @Test
654   public void throw_IAE_if_lines_is_zero_in_report() {
655     ScannerReport.Component project = newBuilder()
656       .setType(PROJECT)
657       .setRef(1)
658       .addChildRef(2)
659       .build();
660     scannerComponentProvider.add(newBuilder()
661       .setRef(2)
662       .setType(FILE)
663       .setPath("src/js/Foo.js")
664       .setLines(0));
665
666     expectedException.expect(IllegalArgumentException.class);
667     expectedException.expectMessage("File 'src/js/Foo.js' has no line");
668
669     call(project);
670   }
671
672   @Test
673   public void throw_IAE_if_lines_is_negative_in_report() {
674     ScannerReport.Component project = newBuilder()
675       .setType(PROJECT)
676       .setRef(1)
677       .addChildRef(2)
678       .build();
679     scannerComponentProvider.add(newBuilder()
680       .setRef(2)
681       .setType(FILE)
682       .setPath("src/js/Foo.js")
683       .setLines(-10));
684
685     expectedException.expect(IllegalArgumentException.class);
686     expectedException.expectMessage("File 'src/js/Foo.js' has no line");
687
688     call(project);
689   }
690
691   private static class ScannerComponentProvider extends ExternalResource implements Function<Integer, ScannerReport.Component> {
692     private final Map<Integer, ScannerReport.Component> components = new HashMap<>();
693
694     @Override
695     protected void before() throws Throwable {
696       components.clear();
697     }
698
699     @Override
700     public ScannerReport.Component apply(Integer componentRef) {
701       return Objects.requireNonNull(components.get(componentRef), "No Component for componentRef " + componentRef);
702     }
703
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());
708       return component;
709     }
710   }
711
712   private Component call(ScannerReport.Component project) {
713     return newUnderTest(null).buildProject(project);
714   }
715
716   private Component call(ScannerReport.Component project, @Nullable SnapshotDto baseAnalysis) {
717     return newUnderTest(baseAnalysis).buildProject(project);
718   }
719
720   private ComponentTreeBuilder newUnderTest(@Nullable SnapshotDto baseAnalysis) {
721     return new ComponentTreeBuilder(KEY_GENERATOR, UUID_SUPPLIER, scannerComponentProvider, projectInDb, baseAnalysis);
722   }
723
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) {
728         @Override
729         public void visitAny(Component any) {
730           componentsByRef.put(any.getReportAttributes().getRef(), any);
731         }
732       }).visit(root);
733     return componentsByRef;
734   }
735 }