]> source.dussan.org Git - sonarqube.git/blob
663125a229ece20358d2de9c348a9f811479c270
[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.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;
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 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);
57
58   @Rule
59   public ExpectedException expectedException = ExpectedException.none();
60
61   @Rule
62   public ScannerComponentProvider scannerComponentProvider = new ScannerComponentProvider();
63
64   private Project projectInDb = new Project(UUID_SUPPLIER.apply("K1"), "K1", "theProjectName");
65
66   @Test
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))
71       .forEach(
72         (type) -> {
73           ScannerReport.Component component = newBuilder().setType(type).build();
74           try {
75             call(component);
76             fail("Should have thrown a IllegalArgumentException");
77           } catch (IllegalArgumentException e) {
78             assertThat(e).hasMessage("Unsupported component type '" + type + "'");
79           }
80         });
81   }
82
83   @Test
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()
88       .setType(PROJECT)
89       .setKey(projectInDb.getKey())
90       .setRef(42)
91       .setName(nameInReport)
92       .setDescription(descriptionInReport)
93       .setVersion("6.5")
94       .build());
95
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);
106   }
107
108   @Test
109   public void project_name_is_loaded_from_db_if_absent_from_report() {
110     Component root = call(newBuilder()
111       .setType(PROJECT)
112       .build());
113
114     assertThat(root.getName()).isEqualTo(projectInDb.getName());
115   }
116
117   @Test
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()
121       .setType(PROJECT)
122       .build(), baseAnalysis);
123
124     assertThat(root.getReportAttributes().getVersion()).isEqualTo("6.5");
125   }
126
127   @Test
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()
131       .setType(PROJECT)
132       .setVersion("")
133       .build(), baseAnalysis);
134
135     assertThat(root.getReportAttributes().getVersion()).isEqualTo("6.5");
136   }
137
138   @Test
139   public void project_version_is_hardcoded_if_absent_from_report_and_db() {
140     Component root = call(newBuilder()
141       .setType(PROJECT)
142       .build());
143
144     assertThat(root.getReportAttributes().getVersion()).isEqualTo("not provided");
145   }
146
147   @Test
148   public void project_description_is_null_if_absent_from_report() {
149     Component root = call(newBuilder()
150       .setType(PROJECT)
151       .build());
152
153     assertThat(root.getDescription()).isNull();
154   }
155
156   @Test
157   public void keys_of_module_directory_and_file_are_generated() {
158     ScannerReport.Component project = newBuilder()
159       .setType(PROJECT)
160       .setKey(projectInDb.getKey())
161       .setRef(1)
162       .addChildRef(2)
163       .build();
164     scannerComponentProvider.add(newBuilder()
165       .setRef(2)
166       .setType(MODULE)
167       .setKey("M")
168       .addChildRef(3));
169     scannerComponentProvider.add(newBuilder()
170       .setRef(3)
171       .setType(DIRECTORY)
172       .setPath("src/js")
173       .addChildRef(4));
174     scannerComponentProvider.add(newBuilder()
175       .setRef(4)
176       .setType(FILE)
177       .setPath("src/js/Foo.js")
178       .setLines(1));
179
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);
184
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);
189
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);
194
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();
199   }
200
201   @Test
202   public void names_of_module_directory_and_file_are_public_keys_if_names_are_absent_from_report() {
203     ScannerReport.Component project = newBuilder()
204       .setType(PROJECT)
205       .setKey(projectInDb.getKey())
206       .setRef(1)
207       .addChildRef(2)
208       .build();
209     scannerComponentProvider.add(newBuilder()
210       .setRef(2)
211       .setType(MODULE)
212       .setKey("M")
213       .addChildRef(3));
214     scannerComponentProvider.add(newBuilder()
215       .setRef(3)
216       .setType(DIRECTORY)
217       .setPath("src/js")
218       .addChildRef(4));
219     scannerComponentProvider.add(newBuilder()
220       .setRef(4)
221       .setType(FILE)
222       .setPath("src/js/Foo.js")
223       .setLines(1));
224
225     Component root = call(project);
226
227     Component module = root.getChildren().iterator().next();
228     assertThat(module.getName()).isEqualTo("public_M");
229
230     Component directory = module.getChildren().iterator().next();
231     assertThat(directory.getName()).isEqualTo("public_M:src/js");
232
233     Component file = directory.getChildren().iterator().next();
234     assertThat(file.getName()).isEqualTo("public_M:src/js/Foo.js");
235   }
236
237   @Test
238   public void names_of_module_directory_and_file_are_public_keys_if_names_are_empty_in_report() {
239     ScannerReport.Component project = newBuilder()
240       .setType(PROJECT)
241       .setKey(projectInDb.getKey())
242       .setRef(1)
243       .addChildRef(2)
244       .build();
245     scannerComponentProvider.add(newBuilder()
246       .setRef(2)
247       .setType(MODULE)
248       .setKey("M")
249       .setName("")
250       .addChildRef(3));
251     scannerComponentProvider.add(newBuilder()
252       .setRef(3)
253       .setType(DIRECTORY)
254       .setPath("src/js")
255       .setName("")
256       .addChildRef(4));
257     scannerComponentProvider.add(newBuilder()
258       .setRef(4)
259       .setType(FILE)
260       .setPath("src/js/Foo.js")
261       .setName("")
262       .setLines(1));
263
264     Component root = call(project);
265
266     Component module = root.getChildren().iterator().next();
267     assertThat(module.getName()).isEqualTo("public_M");
268
269     Component directory = module.getChildren().iterator().next();
270     assertThat(directory.getName()).isEqualTo("public_M:src/js");
271
272     Component file = directory.getChildren().iterator().next();
273     assertThat(file.getName()).isEqualTo("public_M:src/js/Foo.js");
274   }
275
276   private void assertThatFileAttributesAreNotSet(Component root) {
277     try {
278       root.getFileAttributes();
279       fail();
280     } catch (IllegalStateException e) {
281       assertThat(e).hasMessage("Only component of type FILE have a FileAttributes object");
282     }
283   }
284
285   @Test
286   public void keys_of_module_directory_and_files_includes_name_of_closest_module() {
287     ScannerReport.Component project = newBuilder()
288       .setType(PROJECT)
289       .setKey("project 1")
290       .setRef(1)
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));
307
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:");
340   }
341
342   @Test
343   public void uuids_are_provided_by_supplier() {
344     ScannerReport.Component project = newBuilder()
345       .setType(PROJECT)
346       .setKey("c1")
347       .setRef(1)
348       .addChildRef(2)
349       .build();
350     scannerComponentProvider.add(newBuilder()
351       .setRef(2)
352       .setType(MODULE)
353       .setKey("c2")
354       .addChildRef(3));
355     scannerComponentProvider.add(newBuilder()
356       .setRef(3)
357       .setType(DIRECTORY)
358       .setPath("src/js")
359       .addChildRef(4));
360     scannerComponentProvider.add(newBuilder()
361       .setRef(4)
362       .setType(FILE)
363       .setPath("src/js/Foo.js")
364       .setLines(1));
365
366     Component root = call(project);
367     assertThat(root.getUuid()).isEqualTo("generated_c1_uuid");
368
369     Component module = root.getChildren().iterator().next();
370     assertThat(module.getUuid()).isEqualTo("generated_c2_uuid");
371
372     Component directory = module.getChildren().iterator().next();
373     assertThat(directory.getUuid()).isEqualTo("generated_c2:src/js_uuid");
374
375     Component file = directory.getChildren().iterator().next();
376     assertThat(file.getUuid()).isEqualTo("generated_c2:src/js/Foo.js_uuid");
377   }
378
379   @Test
380   public void descriptions_of_module_directory_and_file_are_null_if_absent_from_report() {
381     ScannerReport.Component project = newBuilder()
382       .setType(PROJECT)
383       .setRef(1)
384       .addChildRef(2)
385       .build();
386     scannerComponentProvider.add(newBuilder()
387       .setRef(2)
388       .setType(MODULE)
389       .addChildRef(3));
390     scannerComponentProvider.add(newBuilder()
391       .setRef(3)
392       .setType(DIRECTORY)
393       .setPath("src/js")
394       .addChildRef(4));
395     scannerComponentProvider.add(newBuilder()
396       .setRef(4)
397       .setType(FILE)
398       .setPath("src/js/Foo.js")
399       .setLines(1));
400
401     Component root = call(project);
402
403     Component module = root.getChildren().iterator().next();
404     assertThat(module.getDescription()).isNull();
405
406     Component directory = module.getChildren().iterator().next();
407     assertThat(directory.getDescription()).isNull();
408
409     Component file = directory.getChildren().iterator().next();
410     assertThat(file.getDescription()).isNull();
411   }
412
413   @Test
414   public void descriptions_of_module_directory_and_file_are_null_if_empty_in_report() {
415     ScannerReport.Component project = newBuilder()
416       .setType(PROJECT)
417       .setRef(1)
418       .setDescription("")
419       .addChildRef(2)
420       .build();
421     scannerComponentProvider.add(newBuilder()
422       .setRef(2)
423       .setType(MODULE)
424       .setDescription("")
425       .addChildRef(3));
426     scannerComponentProvider.add(newBuilder()
427       .setRef(3)
428       .setType(DIRECTORY)
429       .setDescription("")
430       .setPath("src/js")
431       .addChildRef(4));
432     scannerComponentProvider.add(newBuilder()
433       .setRef(4)
434       .setType(FILE)
435       .setDescription("")
436       .setPath("src/js/Foo.js")
437       .setLines(1));
438
439     Component root = call(project);
440
441     Component module = root.getChildren().iterator().next();
442     assertThat(module.getDescription()).isNull();
443
444     Component directory = module.getChildren().iterator().next();
445     assertThat(directory.getDescription()).isNull();
446
447     Component file = directory.getChildren().iterator().next();
448     assertThat(file.getDescription()).isNull();
449   }
450
451   @Test
452   public void descriptions_of_module_directory_and_file_are_set_from_report_if_present() {
453     ScannerReport.Component project = newBuilder()
454       .setType(PROJECT)
455       .setRef(1)
456       .addChildRef(2)
457       .build();
458     scannerComponentProvider.add(newBuilder()
459       .setRef(2)
460       .setType(MODULE)
461       .setDescription("b")
462       .addChildRef(3));
463     scannerComponentProvider.add(newBuilder()
464       .setRef(3)
465       .setType(DIRECTORY)
466       .setDescription("c")
467       .setPath("src/js")
468       .addChildRef(4));
469     scannerComponentProvider.add(newBuilder()
470       .setRef(4)
471       .setType(FILE)
472       .setDescription("d")
473       .setPath("src/js/Foo.js")
474       .setLines(1));
475
476     Component root = call(project);
477
478     Component module = root.getChildren().iterator().next();
479     assertThat(module.getDescription()).isEqualTo("b");
480
481     Component directory = module.getChildren().iterator().next();
482     assertThat(directory.getDescription()).isEqualTo("c");
483
484     Component file = directory.getChildren().iterator().next();
485     assertThat(file.getDescription()).isEqualTo("d");
486   }
487
488   @Test
489   public void versions_of_module_directory_and_file_are_set_from_report_if_present() {
490     ScannerReport.Component project = newBuilder()
491       .setType(PROJECT)
492       .setRef(1)
493       .addChildRef(2)
494       .build();
495     scannerComponentProvider.add(newBuilder()
496       .setRef(2)
497       .setType(MODULE)
498       .setVersion("v1")
499       .addChildRef(3));
500     scannerComponentProvider.add(newBuilder()
501       .setRef(3)
502       .setType(DIRECTORY)
503       .setVersion("v2")
504       .setPath("src/js")
505       .addChildRef(4));
506     scannerComponentProvider.add(newBuilder()
507       .setRef(4)
508       .setType(FILE)
509       .setVersion("v3")
510       .setPath("src/js/Foo.js")
511       .setLines(1));
512
513     Component root = call(project);
514
515     Component module = root.getChildren().iterator().next();
516     assertThat(module.getReportAttributes().getVersion()).isEqualTo("v1");
517
518     Component directory = module.getChildren().iterator().next();
519     assertThat(directory.getReportAttributes().getVersion()).isEqualTo("v2");
520
521     Component file = directory.getChildren().iterator().next();
522     assertThat(file.getReportAttributes().getVersion()).isEqualTo("v3");
523   }
524
525   @Test
526   public void versions_of_module_directory_and_file_are_null_if_absent_from_report() {
527     ScannerReport.Component project = newBuilder()
528       .setType(PROJECT)
529       .setRef(1)
530       .addChildRef(2)
531       .build();
532     scannerComponentProvider.add(newBuilder()
533       .setRef(2)
534       .setType(MODULE)
535       .addChildRef(3));
536     scannerComponentProvider.add(newBuilder()
537       .setRef(3)
538       .setType(DIRECTORY)
539       .setPath("src/js")
540       .addChildRef(4));
541     scannerComponentProvider.add(newBuilder()
542       .setRef(4)
543       .setType(FILE)
544       .setPath("src/js/Foo.js")
545       .setLines(1));
546
547     Component root = call(project);
548
549     Component module = root.getChildren().iterator().next();
550     assertThat(module.getReportAttributes().getVersion()).isNull();
551
552     Component directory = module.getChildren().iterator().next();
553     assertThat(directory.getReportAttributes().getVersion()).isNull();
554
555     Component file = directory.getChildren().iterator().next();
556     assertThat(file.getReportAttributes().getVersion()).isNull();
557   }
558
559   @Test
560   public void versions_of_module_directory_and_file_are_null_if_empty_in_report() {
561     ScannerReport.Component project = newBuilder()
562       .setType(PROJECT)
563       .setRef(1)
564       .addChildRef(2)
565       .build();
566     scannerComponentProvider.add(newBuilder()
567       .setRef(2)
568       .setType(MODULE)
569       .setVersion("")
570       .addChildRef(3));
571     scannerComponentProvider.add(newBuilder()
572       .setRef(3)
573       .setType(DIRECTORY)
574       .setVersion("")
575       .setPath("src/js")
576       .addChildRef(4));
577     scannerComponentProvider.add(newBuilder()
578       .setRef(4)
579       .setType(FILE)
580       .setVersion("")
581       .setPath("src/js/Foo.js")
582       .setLines(1));
583
584     Component root = call(project);
585
586     Component module = root.getChildren().iterator().next();
587     assertThat(module.getReportAttributes().getVersion()).isNull();
588
589     Component directory = module.getChildren().iterator().next();
590     assertThat(directory.getReportAttributes().getVersion()).isNull();
591
592     Component file = directory.getChildren().iterator().next();
593     assertThat(file.getReportAttributes().getVersion()).isNull();
594   }
595
596   @Test
597   public void only_nb_of_lines_is_mandatory_on_file_attributes() {
598     ScannerReport.Component project = newBuilder()
599       .setType(PROJECT)
600       .setRef(1)
601       .addChildRef(2)
602       .build();
603     scannerComponentProvider.add(newBuilder()
604       .setRef(2)
605       .setType(FILE)
606       .setPath("src/js/Foo.js")
607       .setLines(1));
608
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();
614   }
615
616   @Test
617   public void language_file_attributes_is_null_if_empty_in_report() {
618     ScannerReport.Component project = newBuilder()
619       .setType(PROJECT)
620       .setRef(1)
621       .addChildRef(2)
622       .build();
623     scannerComponentProvider.add(newBuilder()
624       .setRef(2)
625       .setType(FILE)
626       .setPath("src/js/Foo.js")
627       .setLines(1)
628       .setLanguage(""));
629
630     Component root = call(project);
631     Component file = root.getChildren().iterator().next();
632     assertThat(file.getFileAttributes().getLanguageKey()).isNull();
633   }
634
635   @Test
636   public void file_attributes_are_fully_loaded_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       .setLines(1)
647       .setLanguage("js")
648       .setIsTest(true));
649
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();
655   }
656
657   @Test
658   public void throw_IAE_if_lines_is_absent_from_report() {
659     ScannerReport.Component project = newBuilder()
660       .setType(PROJECT)
661       .setRef(1)
662       .addChildRef(2)
663       .build();
664     scannerComponentProvider.add(newBuilder()
665       .setRef(2)
666       .setType(FILE)
667       .setPath("src/js/Foo.js"));
668
669     expectedException.expect(IllegalArgumentException.class);
670     expectedException.expectMessage("File 'src/js/Foo.js' has no line");
671
672     call(project);
673   }
674
675   @Test
676   public void throw_IAE_if_lines_is_zero_in_report() {
677     ScannerReport.Component project = newBuilder()
678       .setType(PROJECT)
679       .setRef(1)
680       .addChildRef(2)
681       .build();
682     scannerComponentProvider.add(newBuilder()
683       .setRef(2)
684       .setType(FILE)
685       .setPath("src/js/Foo.js")
686       .setLines(0));
687
688     expectedException.expect(IllegalArgumentException.class);
689     expectedException.expectMessage("File 'src/js/Foo.js' has no line");
690
691     call(project);
692   }
693
694   @Test
695   public void throw_IAE_if_lines_is_negative_in_report() {
696     ScannerReport.Component project = newBuilder()
697       .setType(PROJECT)
698       .setRef(1)
699       .addChildRef(2)
700       .build();
701     scannerComponentProvider.add(newBuilder()
702       .setRef(2)
703       .setType(FILE)
704       .setPath("src/js/Foo.js")
705       .setLines(-10));
706
707     expectedException.expect(IllegalArgumentException.class);
708     expectedException.expectMessage("File 'src/js/Foo.js' has no line");
709
710     call(project);
711   }
712
713   private static class ScannerComponentProvider extends ExternalResource implements Function<Integer, ScannerReport.Component> {
714     private final Map<Integer, ScannerReport.Component> components = new HashMap<>();
715
716     @Override
717     protected void before() throws Throwable {
718       components.clear();
719     }
720
721     @Override
722     public ScannerReport.Component apply(Integer componentRef) {
723       return Objects.requireNonNull(components.get(componentRef), "No Component for componentRef " + componentRef);
724     }
725
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());
730       return component;
731     }
732   }
733
734   private Component call(ScannerReport.Component project) {
735     return newUnderTest(null).buildProject(project);
736   }
737
738   private Component call(ScannerReport.Component project, @Nullable SnapshotDto baseAnalysis) {
739     return newUnderTest(baseAnalysis).buildProject(project);
740   }
741
742   private ComponentTreeBuilder newUnderTest(@Nullable SnapshotDto baseAnalysis) {
743     return new ComponentTreeBuilder(KEY_GENERATOR, PUBLIC_KEY_GENERATOR, UUID_SUPPLIER, scannerComponentProvider, projectInDb, baseAnalysis);
744   }
745
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) {
750         @Override
751         public void visitAny(Component any) {
752           componentsByRef.put(any.getReportAttributes().getRef(), any);
753         }
754       }).visit(root);
755     return componentsByRef;
756   }
757 }