You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

DefaultSensorStorageTest.java 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330
  1. /*
  2. * SonarQube
  3. * Copyright (C) 2009-2019 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.scanner.sensor;
  21. import com.google.common.collect.ImmutableMap;
  22. import java.io.IOException;
  23. import java.util.Map;
  24. import org.junit.Before;
  25. import org.junit.Rule;
  26. import org.junit.Test;
  27. import org.junit.rules.ExpectedException;
  28. import org.junit.rules.TemporaryFolder;
  29. import org.mockito.ArgumentCaptor;
  30. import org.sonar.api.batch.bootstrap.ProjectDefinition;
  31. import org.sonar.api.batch.fs.InputFile;
  32. import org.sonar.api.batch.fs.internal.DefaultInputFile;
  33. import org.sonar.api.batch.fs.internal.DefaultInputModule;
  34. import org.sonar.api.batch.fs.internal.TestInputFileBuilder;
  35. import org.sonar.api.batch.measure.MetricFinder;
  36. import org.sonar.api.batch.sensor.code.internal.DefaultSignificantCode;
  37. import org.sonar.api.batch.sensor.highlighting.TypeOfText;
  38. import org.sonar.api.batch.sensor.highlighting.internal.DefaultHighlighting;
  39. import org.sonar.api.batch.sensor.issue.ExternalIssue;
  40. import org.sonar.api.batch.sensor.issue.Issue;
  41. import org.sonar.api.batch.sensor.issue.internal.DefaultExternalIssue;
  42. import org.sonar.api.batch.sensor.issue.internal.DefaultIssue;
  43. import org.sonar.api.batch.sensor.issue.internal.DefaultIssueLocation;
  44. import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure;
  45. import org.sonar.api.batch.sensor.symbol.internal.DefaultSymbolTable;
  46. import org.sonar.api.config.internal.MapSettings;
  47. import org.sonar.api.measures.CoreMetrics;
  48. import org.sonar.api.utils.KeyValueFormat;
  49. import org.sonar.core.metric.ScannerMetrics;
  50. import org.sonar.scanner.cpd.index.SonarCpdBlockIndex;
  51. import org.sonar.scanner.issue.ModuleIssues;
  52. import org.sonar.scanner.protocol.output.FileStructure;
  53. import org.sonar.scanner.protocol.output.ScannerReportWriter;
  54. import org.sonar.scanner.report.ReportPublisher;
  55. import org.sonar.scanner.repository.ContextPropertiesCache;
  56. import org.sonar.scanner.scan.branch.BranchConfiguration;
  57. import org.sonar.scanner.scan.measure.MeasureCache;
  58. import static org.assertj.core.api.Assertions.assertThat;
  59. import static org.assertj.core.data.MapEntry.entry;
  60. import static org.mockito.ArgumentMatchers.eq;
  61. import static org.mockito.Mockito.mock;
  62. import static org.mockito.Mockito.verify;
  63. import static org.mockito.Mockito.verifyZeroInteractions;
  64. import static org.mockito.Mockito.when;
  65. public class DefaultSensorStorageTest {
  66. @Rule
  67. public TemporaryFolder temp = new TemporaryFolder();
  68. @Rule
  69. public ExpectedException thrown = ExpectedException.none();
  70. private DefaultSensorStorage underTest;
  71. private MapSettings settings;
  72. private ModuleIssues moduleIssues;
  73. private MeasureCache measureCache;
  74. private ScannerReportWriter reportWriter;
  75. private ContextPropertiesCache contextPropertiesCache = new ContextPropertiesCache();
  76. private BranchConfiguration branchConfiguration;
  77. private DefaultInputModule projectRoot;
  78. @Before
  79. public void prepare() throws Exception {
  80. MetricFinder metricFinder = mock(MetricFinder.class);
  81. when(metricFinder.<Integer>findByKey(CoreMetrics.NCLOC_KEY)).thenReturn(CoreMetrics.NCLOC);
  82. when(metricFinder.<String>findByKey(CoreMetrics.FUNCTION_COMPLEXITY_DISTRIBUTION_KEY)).thenReturn(CoreMetrics.FUNCTION_COMPLEXITY_DISTRIBUTION);
  83. when(metricFinder.<Integer>findByKey(CoreMetrics.LINES_TO_COVER_KEY)).thenReturn(CoreMetrics.LINES_TO_COVER);
  84. settings = new MapSettings();
  85. moduleIssues = mock(ModuleIssues.class);
  86. measureCache = mock(MeasureCache.class);
  87. ReportPublisher reportPublisher = mock(ReportPublisher.class);
  88. reportWriter = new ScannerReportWriter(temp.newFolder());
  89. when(reportPublisher.getWriter()).thenReturn(reportWriter);
  90. branchConfiguration = mock(BranchConfiguration.class);
  91. underTest = new DefaultSensorStorage(metricFinder,
  92. moduleIssues, settings.asConfig(), reportPublisher, measureCache,
  93. mock(SonarCpdBlockIndex.class), contextPropertiesCache, new ScannerMetrics(), branchConfiguration);
  94. projectRoot = new DefaultInputModule(ProjectDefinition.create()
  95. .setKey("foo")
  96. .setBaseDir(temp.newFolder())
  97. .setWorkDir(temp.newFolder()));
  98. }
  99. @Test
  100. public void shouldFailIfUnknownMetric() {
  101. InputFile file = new TestInputFileBuilder("foo", "src/Foo.php").build();
  102. thrown.expect(UnsupportedOperationException.class);
  103. thrown.expectMessage("Unknown metric: lines");
  104. underTest.store(new DefaultMeasure()
  105. .on(file)
  106. .forMetric(CoreMetrics.LINES)
  107. .withValue(10));
  108. }
  109. @Test
  110. public void should_save_issue() {
  111. InputFile file = new TestInputFileBuilder("foo", "src/Foo.php").build();
  112. DefaultIssue issue = new DefaultIssue(projectRoot).at(new DefaultIssueLocation().on(file));
  113. underTest.store(issue);
  114. ArgumentCaptor<Issue> argumentCaptor = ArgumentCaptor.forClass(Issue.class);
  115. verify(moduleIssues).initAndAddIssue(argumentCaptor.capture());
  116. assertThat(argumentCaptor.getValue()).isEqualTo(issue);
  117. }
  118. @Test
  119. public void should_save_external_issue() {
  120. InputFile file = new TestInputFileBuilder("foo", "src/Foo.php").build();
  121. DefaultExternalIssue externalIssue = new DefaultExternalIssue(projectRoot).at(new DefaultIssueLocation().on(file));
  122. underTest.store(externalIssue);
  123. ArgumentCaptor<ExternalIssue> argumentCaptor = ArgumentCaptor.forClass(ExternalIssue.class);
  124. verify(moduleIssues).initAndAddExternalIssue(argumentCaptor.capture());
  125. assertThat(argumentCaptor.getValue()).isEqualTo(externalIssue);
  126. }
  127. @Test
  128. public void should_skip_issue_on_short_branch_when_file_status_is_SAME() {
  129. InputFile file = new TestInputFileBuilder("foo", "src/Foo.php").setStatus(InputFile.Status.SAME).build();
  130. when(branchConfiguration.isShortOrPullRequest()).thenReturn(true);
  131. DefaultIssue issue = new DefaultIssue(projectRoot).at(new DefaultIssueLocation().on(file));
  132. underTest.store(issue);
  133. verifyZeroInteractions(moduleIssues);
  134. }
  135. @Test
  136. public void should_save_highlighting() {
  137. DefaultInputFile file = new TestInputFileBuilder("foo", "src/Foo.php")
  138. .setContents("// comment").build();
  139. DefaultHighlighting highlighting = new DefaultHighlighting(underTest).onFile(file).highlight(0, 1, TypeOfText.KEYWORD);
  140. underTest.store(highlighting);
  141. assertThat(reportWriter.hasComponentData(FileStructure.Domain.SYNTAX_HIGHLIGHTINGS, file.batchId())).isTrue();
  142. }
  143. @Test
  144. public void should_skip_highlighting_on_short_branch_when_file_status_is_SAME() {
  145. DefaultInputFile file = new TestInputFileBuilder("foo", "src/Foo.php")
  146. .setContents("// comment")
  147. .setStatus(InputFile.Status.SAME).build();
  148. when(branchConfiguration.isShortOrPullRequest()).thenReturn(true);
  149. DefaultHighlighting highlighting = new DefaultHighlighting(underTest).onFile(file).highlight(0, 1, TypeOfText.KEYWORD);
  150. underTest.store(highlighting);
  151. assertThat(reportWriter.hasComponentData(FileStructure.Domain.SYNTAX_HIGHLIGHTINGS, file.batchId())).isFalse();
  152. }
  153. @Test
  154. public void should_save_file_measure() {
  155. InputFile file = new TestInputFileBuilder("foo", "src/Foo.php").build();
  156. ArgumentCaptor<DefaultMeasure> argumentCaptor = ArgumentCaptor.forClass(DefaultMeasure.class);
  157. when(measureCache.put(eq(file.key()), eq(CoreMetrics.NCLOC_KEY), argumentCaptor.capture())).thenReturn(null);
  158. underTest.store(new DefaultMeasure()
  159. .on(file)
  160. .forMetric(CoreMetrics.NCLOC)
  161. .withValue(10));
  162. DefaultMeasure m = argumentCaptor.getValue();
  163. assertThat(m.value()).isEqualTo(10);
  164. assertThat(m.metric()).isEqualTo(CoreMetrics.NCLOC);
  165. }
  166. @Test
  167. public void should_not_skip_file_measures_on_short_lived_branch_or_pull_request_when_file_status_is_SAME() {
  168. InputFile file = new TestInputFileBuilder("foo", "src/Foo.php").setStatus(InputFile.Status.SAME).build();
  169. when(branchConfiguration.isShortOrPullRequest()).thenReturn(true);
  170. ArgumentCaptor<DefaultMeasure> argumentCaptor = ArgumentCaptor.forClass(DefaultMeasure.class);
  171. when(measureCache.put(eq(file.key()), eq(CoreMetrics.LINES_TO_COVER_KEY), argumentCaptor.capture())).thenReturn(null);
  172. underTest.store(new DefaultMeasure()
  173. .on(file)
  174. .forMetric(CoreMetrics.LINES_TO_COVER)
  175. .withValue(10));
  176. DefaultMeasure m = argumentCaptor.getValue();
  177. assertThat(m.value()).isEqualTo(10);
  178. assertThat(m.metric()).isEqualTo(CoreMetrics.LINES_TO_COVER);
  179. }
  180. @Test
  181. public void should_skip_significant_code_on_pull_request_when_file_status_is_SAME() {
  182. DefaultInputFile file = new TestInputFileBuilder("foo", "src/Foo.php")
  183. .setStatus(InputFile.Status.SAME)
  184. .setContents("foo")
  185. .build();
  186. when(branchConfiguration.isShortOrPullRequest()).thenReturn(true);
  187. underTest.store(new DefaultSignificantCode()
  188. .onFile(file)
  189. .addRange(file.selectLine(1)));
  190. assertThat(reportWriter.hasComponentData(FileStructure.Domain.SGNIFICANT_CODE, file.batchId())).isFalse();
  191. }
  192. @Test
  193. public void should_save_significant_code() {
  194. DefaultInputFile file = new TestInputFileBuilder("foo", "src/Foo.php")
  195. .setContents("foo")
  196. .build();
  197. underTest.store(new DefaultSignificantCode()
  198. .onFile(file)
  199. .addRange(file.selectLine(1)));
  200. assertThat(reportWriter.hasComponentData(FileStructure.Domain.SGNIFICANT_CODE, file.batchId())).isTrue();
  201. }
  202. @Test
  203. public void should_save_project_measure() throws IOException {
  204. String projectKey = "myProject";
  205. DefaultInputModule module = new DefaultInputModule(ProjectDefinition.create().setKey(projectKey).setBaseDir(temp.newFolder()).setWorkDir(temp.newFolder()));
  206. ArgumentCaptor<DefaultMeasure> argumentCaptor = ArgumentCaptor.forClass(DefaultMeasure.class);
  207. when(measureCache.put(eq(module.key()), eq(CoreMetrics.NCLOC_KEY), argumentCaptor.capture())).thenReturn(null);
  208. underTest.store(new DefaultMeasure()
  209. .on(module)
  210. .forMetric(CoreMetrics.NCLOC)
  211. .withValue(10));
  212. DefaultMeasure m = argumentCaptor.getValue();
  213. assertThat(m.value()).isEqualTo(10);
  214. assertThat(m.metric()).isEqualTo(CoreMetrics.NCLOC);
  215. }
  216. @Test(expected = UnsupportedOperationException.class)
  217. public void duplicateHighlighting() throws Exception {
  218. InputFile inputFile = new TestInputFileBuilder("foo", "src/Foo.java")
  219. .setModuleBaseDir(temp.newFolder().toPath()).build();
  220. DefaultHighlighting h = new DefaultHighlighting(null)
  221. .onFile(inputFile);
  222. underTest.store(h);
  223. underTest.store(h);
  224. }
  225. @Test(expected = UnsupportedOperationException.class)
  226. public void duplicateSignificantCode() throws Exception {
  227. InputFile inputFile = new TestInputFileBuilder("foo", "src/Foo.java")
  228. .setModuleBaseDir(temp.newFolder().toPath()).build();
  229. DefaultSignificantCode h = new DefaultSignificantCode(null)
  230. .onFile(inputFile);
  231. underTest.store(h);
  232. underTest.store(h);
  233. }
  234. @Test(expected = UnsupportedOperationException.class)
  235. public void duplicateSymbolTable() throws Exception {
  236. InputFile inputFile = new TestInputFileBuilder("foo", "src/Foo.java")
  237. .setModuleBaseDir(temp.newFolder().toPath()).build();
  238. DefaultSymbolTable st = new DefaultSymbolTable(null)
  239. .onFile(inputFile);
  240. underTest.store(st);
  241. underTest.store(st);
  242. }
  243. @Test
  244. public void shouldStoreContextProperty() {
  245. underTest.storeProperty("foo", "bar");
  246. assertThat(contextPropertiesCache.getAll()).containsOnly(entry("foo", "bar"));
  247. }
  248. @Test
  249. public void shouldValidateStrictlyPositiveLine() throws Exception {
  250. InputFile file = new TestInputFileBuilder("module", "testfile").setModuleBaseDir(temp.newFolder().toPath()).build();
  251. Map<Integer, Integer> map = ImmutableMap.of(0, 3);
  252. String data = KeyValueFormat.format(map);
  253. thrown.expect(IllegalStateException.class);
  254. thrown.expectMessage("must be > 0");
  255. underTest.validateCoverageMeasure(data, file);
  256. }
  257. @Test
  258. public void shouldValidateMaxLine() throws Exception {
  259. InputFile file = new TestInputFileBuilder("module", "testfile").setModuleBaseDir(temp.newFolder().toPath()).build();
  260. Map<Integer, Integer> map = ImmutableMap.of(11, 3);
  261. String data = KeyValueFormat.format(map);
  262. thrown.expect(IllegalStateException.class);
  263. underTest.validateCoverageMeasure(data, file);
  264. }
  265. @Test
  266. public void mergeCoverageLineMetrics_should_be_sorted() {
  267. assertThat(DefaultSensorStorage.mergeCoverageLineMetric(CoreMetrics.COVERAGE_LINE_HITS_DATA, "1=1", "1=1")).isEqualTo("1=2");
  268. assertThat(DefaultSensorStorage.mergeCoverageLineMetric(CoreMetrics.COVERAGE_LINE_HITS_DATA, "1=1", "2=1")).isEqualTo("1=1;2=1");
  269. assertThat(DefaultSensorStorage.mergeCoverageLineMetric(CoreMetrics.COVERAGE_LINE_HITS_DATA, "2=1", "1=1")).isEqualTo("1=1;2=1");
  270. assertThat(DefaultSensorStorage.mergeCoverageLineMetric(CoreMetrics.COVERED_CONDITIONS_BY_LINE, "1=1", "1=1")).isEqualTo("1=1");
  271. assertThat(DefaultSensorStorage.mergeCoverageLineMetric(CoreMetrics.COVERED_CONDITIONS_BY_LINE, "1=1", "2=1")).isEqualTo("1=1;2=1");
  272. assertThat(DefaultSensorStorage.mergeCoverageLineMetric(CoreMetrics.COVERED_CONDITIONS_BY_LINE, "2=1", "1=1")).isEqualTo("1=1;2=1");
  273. }
  274. }