]> source.dussan.org Git - sonarqube.git/blob
356e33c0b5ab1a04f708b3ef658d85653702640f
[sonarqube.git] /
1 /*
2  * SonarQube
3  * Copyright (C) 2009-2016 SonarSource SA
4  * mailto:contact 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.step;
21
22 import java.util.*;
23
24 import org.junit.Before;
25 import org.junit.Rule;
26 import org.junit.Test;
27 import org.junit.rules.ExpectedException;
28 import org.mockito.ArgumentCaptor;
29 import org.sonar.api.utils.System2;
30 import org.sonar.db.DbClient;
31 import org.sonar.db.DbSession;
32 import org.sonar.db.DbTester;
33 import org.sonar.db.component.ComponentDto;
34 import org.sonar.db.component.ComponentTesting;
35 import org.sonar.db.component.SnapshotDto;
36 import org.sonar.db.component.SnapshotTesting;
37 import org.sonar.db.duplication.DuplicationUnitDto;
38 import org.sonar.duplications.block.Block;
39 import org.sonar.duplications.block.ByteArray;
40 import org.sonar.scanner.protocol.output.ScannerReport;
41 import org.sonar.server.computation.analysis.AnalysisMetadataHolderRule;
42 import org.sonar.server.computation.batch.BatchReportReaderRule;
43 import org.sonar.server.computation.batch.TreeRootHolderRule;
44 import org.sonar.server.computation.component.Component;
45 import org.sonar.server.computation.component.FileAttributes;
46 import org.sonar.server.computation.component.ReportComponent;
47 import org.sonar.server.computation.duplication.CrossProjectDuplicationStatusHolder;
48 import org.sonar.server.computation.duplication.IntegrateCrossProjectDuplications;
49 import org.sonar.server.computation.snapshot.Snapshot;
50
51 import static java.util.Arrays.asList;
52 import static org.assertj.core.api.Assertions.assertThat;
53 import static org.mockito.Mockito.*;
54 import static org.sonar.server.computation.component.Component.Type.FILE;
55 import static org.sonar.server.computation.component.Component.Type.PROJECT;
56
57 public class LoadCrossProjectDuplicationsRepositoryStepTest {
58
59   @Rule
60   public ExpectedException thrown = ExpectedException.none();
61
62   static final String XOO_LANGUAGE = "xoo";
63
64   static final int PROJECT_REF = 1;
65   static final int FILE_REF = 2;
66   static final String CURRENT_FILE_KEY = "FILE_KEY";
67
68   static final Component CURRENT_FILE = ReportComponent.builder(FILE, FILE_REF)
69     .setKey(CURRENT_FILE_KEY)
70     .setFileAttributes(new FileAttributes(false, XOO_LANGUAGE))
71     .build();
72
73   @Rule
74   public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule().setRoot(
75     ReportComponent.builder(PROJECT, PROJECT_REF)
76       .addChildren(CURRENT_FILE
77       ).build());
78
79   @Rule
80   public BatchReportReaderRule batchReportReader = new BatchReportReaderRule();
81
82   @Rule
83   public AnalysisMetadataHolderRule analysisMetadataHolder = new AnalysisMetadataHolderRule();
84
85   CrossProjectDuplicationStatusHolder crossProjectDuplicationStatusHolder = mock(CrossProjectDuplicationStatusHolder.class);
86
87   @Rule
88   public DbTester dbTester = DbTester.create(System2.INSTANCE);
89
90   DbClient dbClient = dbTester.getDbClient();
91
92   DbSession dbSession = dbTester.getSession();
93
94   IntegrateCrossProjectDuplications integrateCrossProjectDuplications = mock(IntegrateCrossProjectDuplications.class);
95
96   Snapshot baseProjectSnapshot;
97
98   ComputationStep underTest = new LoadCrossProjectDuplicationsRepositoryStep(treeRootHolder, batchReportReader, analysisMetadataHolder, crossProjectDuplicationStatusHolder,
99     integrateCrossProjectDuplications, dbClient);
100
101   @Before
102   public void setUp() throws Exception {
103     ComponentDto project = ComponentTesting.newProjectDto();
104     dbClient.componentDao().insert(dbSession, project);
105     SnapshotDto projectSnapshot = SnapshotTesting.newSnapshotForProject(project);
106     dbClient.snapshotDao().insert(dbSession, projectSnapshot);
107     dbSession.commit();
108
109     baseProjectSnapshot = new Snapshot.Builder()
110       .setId(projectSnapshot.getId())
111       .setCreatedAt(projectSnapshot.getCreatedAt())
112       .build();
113   }
114
115   @Test
116   public void call_compute_cpd_on_one_duplication() throws Exception {
117     when(crossProjectDuplicationStatusHolder.isEnabled()).thenReturn(true);
118     analysisMetadataHolder.setBaseProjectSnapshot(baseProjectSnapshot);
119
120     ComponentDto otherProject = createProject("OTHER_PROJECT_KEY");
121     SnapshotDto otherProjectSnapshot = createProjectSnapshot(otherProject);
122
123     ComponentDto otherFIle = createFile("OTHER_FILE_KEY", otherProject);
124     SnapshotDto otherFileSnapshot = createFileSnapshot(otherFIle, otherProjectSnapshot);
125
126     String hash = "a8998353e96320ec";
127     DuplicationUnitDto duplicate = new DuplicationUnitDto()
128       .setHash(hash)
129       .setStartLine(40)
130       .setEndLine(55)
131       .setIndexInFile(0)
132       .setProjectSnapshotId(otherProjectSnapshot.getId())
133       .setSnapshotId(otherFileSnapshot.getId());
134     dbClient.duplicationDao().insert(dbSession, duplicate);
135     dbSession.commit();
136
137     ScannerReport.CpdTextBlock originBlock = ScannerReport.CpdTextBlock.newBuilder()
138       .setHash(hash)
139       .setStartLine(30)
140       .setEndLine(45)
141       .setStartTokenIndex(0)
142       .setEndTokenIndex(10)
143       .build();
144     batchReportReader.putDuplicationBlocks(FILE_REF, asList(originBlock));
145
146     underTest.execute();
147
148     verify(integrateCrossProjectDuplications).computeCpd(CURRENT_FILE,
149       Arrays.asList(
150         new Block.Builder()
151           .setResourceId(CURRENT_FILE_KEY)
152           .setBlockHash(new ByteArray(hash))
153           .setIndexInFile(0)
154           .setLines(originBlock.getStartLine(), originBlock.getEndLine())
155           .setUnit(originBlock.getStartTokenIndex(), originBlock.getEndTokenIndex())
156           .build()
157         ),
158       Arrays.asList(
159         new Block.Builder()
160           .setResourceId(otherFIle.getKey())
161           .setBlockHash(new ByteArray(hash))
162           .setIndexInFile(duplicate.getIndexInFile())
163           .setLines(duplicate.getStartLine(), duplicate.getEndLine())
164           .build()
165         )
166       );
167   }
168
169   @Test
170   public void call_compute_cpd_on_many_duplication() throws Exception {
171     when(crossProjectDuplicationStatusHolder.isEnabled()).thenReturn(true);
172     analysisMetadataHolder.setBaseProjectSnapshot(baseProjectSnapshot);
173
174     ComponentDto otherProject = createProject("OTHER_PROJECT_KEY");
175     SnapshotDto otherProjectSnapshot = createProjectSnapshot(otherProject);
176
177     ComponentDto otherFIle = createFile("OTHER_FILE_KEY", otherProject);
178     SnapshotDto otherFileSnapshot = createFileSnapshot(otherFIle, otherProjectSnapshot);
179
180     ScannerReport.CpdTextBlock originBlock1 = ScannerReport.CpdTextBlock.newBuilder()
181       .setHash("a8998353e96320ec")
182       .setStartLine(30)
183       .setEndLine(45)
184       .setStartTokenIndex(0)
185       .setEndTokenIndex(10)
186       .build();
187     ScannerReport.CpdTextBlock originBlock2 = ScannerReport.CpdTextBlock.newBuilder()
188       .setHash("b1234353e96320ff")
189       .setStartLine(10)
190       .setEndLine(25)
191       .setStartTokenIndex(5)
192       .setEndTokenIndex(15)
193       .build();
194     batchReportReader.putDuplicationBlocks(FILE_REF, asList(originBlock1, originBlock2));
195
196     DuplicationUnitDto duplicate1 = new DuplicationUnitDto()
197       .setHash(originBlock1.getHash())
198       .setStartLine(40)
199       .setEndLine(55)
200       .setIndexInFile(0)
201       .setProjectSnapshotId(otherProjectSnapshot.getId())
202       .setSnapshotId(otherFileSnapshot.getId());
203
204     DuplicationUnitDto duplicate2 = new DuplicationUnitDto()
205       .setHash(originBlock2.getHash())
206       .setStartLine(20)
207       .setEndLine(35)
208       .setIndexInFile(1)
209       .setProjectSnapshotId(otherProjectSnapshot.getId())
210       .setSnapshotId(otherFileSnapshot.getId());
211     dbClient.duplicationDao().insert(dbSession, duplicate1);
212     dbClient.duplicationDao().insert(dbSession, duplicate2);
213     dbSession.commit();
214
215     underTest.execute();
216
217     Class<ArrayList<Block>> listClass = (Class<ArrayList<Block>>) (Class) ArrayList.class;
218     ArgumentCaptor<ArrayList<Block>> originBlocks = ArgumentCaptor.forClass(listClass);
219     ArgumentCaptor<ArrayList<Block>> duplicationBlocks = ArgumentCaptor.forClass(listClass);
220
221     verify(integrateCrossProjectDuplications).computeCpd(eq(CURRENT_FILE), originBlocks.capture(), duplicationBlocks.capture());
222
223     Map<Integer, Block> originBlocksByIndex = blocksByIndexInFile(originBlocks.getValue());
224     assertThat(originBlocksByIndex.get(0)).isEqualTo(
225       new Block.Builder()
226         .setResourceId(CURRENT_FILE_KEY)
227         .setBlockHash(new ByteArray(originBlock1.getHash()))
228         .setIndexInFile(0)
229         .setLines(originBlock1.getStartLine(), originBlock1.getEndLine())
230         .setUnit(originBlock1.getStartTokenIndex(), originBlock1.getEndTokenIndex())
231         .build()
232       );
233     assertThat(originBlocksByIndex.get(1)).isEqualTo(
234       new Block.Builder()
235         .setResourceId(CURRENT_FILE_KEY)
236         .setBlockHash(new ByteArray(originBlock2.getHash()))
237         .setIndexInFile(1)
238         .setLines(originBlock2.getStartLine(), originBlock2.getEndLine())
239         .setUnit(originBlock2.getStartTokenIndex(), originBlock2.getEndTokenIndex())
240         .build()
241       );
242
243     Map<Integer, Block> duplicationBlocksByIndex = blocksByIndexInFile(duplicationBlocks.getValue());
244     assertThat(duplicationBlocksByIndex.get(0)).isEqualTo(
245       new Block.Builder()
246         .setResourceId(otherFIle.getKey())
247         .setBlockHash(new ByteArray(originBlock1.getHash()))
248         .setIndexInFile(duplicate1.getIndexInFile())
249         .setLines(duplicate1.getStartLine(), duplicate1.getEndLine())
250         .build()
251       );
252     assertThat(duplicationBlocksByIndex.get(1)).isEqualTo(
253       new Block.Builder()
254         .setResourceId(otherFIle.getKey())
255         .setBlockHash(new ByteArray(originBlock2.getHash()))
256         .setIndexInFile(duplicate2.getIndexInFile())
257         .setLines(duplicate2.getStartLine(), duplicate2.getEndLine())
258         .build()
259       );
260   }
261
262   @Test
263   public void nothing_to_do_when_cross_project_duplication_is_disabled() throws Exception {
264     when(crossProjectDuplicationStatusHolder.isEnabled()).thenReturn(false);
265     analysisMetadataHolder.setBaseProjectSnapshot(baseProjectSnapshot);
266
267     ComponentDto otherProject = createProject("OTHER_PROJECT_KEY");
268     SnapshotDto otherProjectSnapshot = createProjectSnapshot(otherProject);
269
270     ComponentDto otherFIle = createFile("OTHER_FILE_KEY", otherProject);
271     SnapshotDto otherFileSnapshot = createFileSnapshot(otherFIle, otherProjectSnapshot);
272
273     String hash = "a8998353e96320ec";
274     DuplicationUnitDto duplicate = new DuplicationUnitDto()
275       .setHash(hash)
276       .setStartLine(40)
277       .setEndLine(55)
278       .setIndexInFile(0)
279       .setProjectSnapshotId(otherProjectSnapshot.getId())
280       .setSnapshotId(otherFileSnapshot.getId());
281     dbClient.duplicationDao().insert(dbSession, duplicate);
282     dbSession.commit();
283
284     ScannerReport.CpdTextBlock originBlock = ScannerReport.CpdTextBlock.newBuilder()
285       .setHash(hash)
286       .setStartLine(30)
287       .setEndLine(45)
288       .setStartTokenIndex(0)
289       .setEndTokenIndex(10)
290       .build();
291     batchReportReader.putDuplicationBlocks(FILE_REF, asList(originBlock));
292
293     underTest.execute();
294
295     verifyZeroInteractions(integrateCrossProjectDuplications);
296   }
297
298   @Test
299   public void nothing_to_do_when_no_cpd_text_blocks_found() throws Exception {
300     when(crossProjectDuplicationStatusHolder.isEnabled()).thenReturn(true);
301     analysisMetadataHolder.setBaseProjectSnapshot(baseProjectSnapshot);
302
303     batchReportReader.putDuplicationBlocks(FILE_REF, Collections.<ScannerReport.CpdTextBlock>emptyList());
304
305     underTest.execute();
306
307     verifyZeroInteractions(integrateCrossProjectDuplications);
308   }
309
310   @Test
311   public void nothing_to_do_when_cpd_text_blocks_exists_but_no_duplicated_found() throws Exception {
312     when(crossProjectDuplicationStatusHolder.isEnabled()).thenReturn(true);
313     analysisMetadataHolder.setBaseProjectSnapshot(baseProjectSnapshot);
314
315     ScannerReport.CpdTextBlock originBlock = ScannerReport.CpdTextBlock.newBuilder()
316       .setHash("a8998353e96320ec")
317       .setStartLine(30)
318       .setEndLine(45)
319       .setStartTokenIndex(0)
320       .setEndTokenIndex(10)
321       .build();
322     batchReportReader.putDuplicationBlocks(FILE_REF, asList(originBlock));
323
324     underTest.execute();
325
326     verifyZeroInteractions(integrateCrossProjectDuplications);
327   }
328
329   private ComponentDto createProject(String projectKey) {
330     ComponentDto project = ComponentTesting.newProjectDto().setKey(projectKey);
331     dbClient.componentDao().insert(dbSession, project);
332     dbSession.commit();
333     return project;
334   }
335
336   private SnapshotDto createProjectSnapshot(ComponentDto project) {
337     SnapshotDto projectSnapshot = SnapshotTesting.newSnapshotForProject(project);
338     dbClient.snapshotDao().insert(dbSession, projectSnapshot);
339     dbSession.commit();
340     return projectSnapshot;
341   }
342
343   private ComponentDto createFile(String fileKey, ComponentDto project) {
344     ComponentDto file = ComponentTesting.newFileDto(project)
345       .setKey(fileKey)
346       .setLanguage(XOO_LANGUAGE);
347     dbClient.componentDao().insert(dbSession, file);
348     dbSession.commit();
349     return file;
350   }
351
352   private SnapshotDto createFileSnapshot(ComponentDto file, SnapshotDto projectSnapshot) {
353     SnapshotDto fileSnapshot = SnapshotTesting.createForComponent(file, projectSnapshot);
354     dbClient.snapshotDao().insert(dbSession, fileSnapshot);
355     dbSession.commit();
356     return fileSnapshot;
357   }
358
359   private static Map<Integer, Block> blocksByIndexInFile(List<Block> blocks) {
360     Map<Integer, Block> blocksByIndexInFile = new HashMap<>();
361     for (Block block : blocks) {
362       blocksByIndexInFile.put(block.getIndexInFile(), block);
363     }
364     return blocksByIndexInFile;
365   }
366
367 }