diff options
author | Julien HENRY <julien.henry@sonarsource.com> | 2016-03-17 17:05:56 +0100 |
---|---|---|
committer | Julien HENRY <julien.henry@sonarsource.com> | 2016-03-18 09:35:05 +0100 |
commit | cfcbe278f7ced12599d898d50f3fe68bfbf95155 (patch) | |
tree | 5b4116ad08a8ba87ffc5bf9f159a431b9609b48f /sonar-scanner-engine/src/test/java | |
parent | 38ce80934961773a9a35ec0401994b3d726597dc (diff) | |
download | sonarqube-cfcbe278f7ced12599d898d50f3fe68bfbf95155.tar.gz sonarqube-cfcbe278f7ced12599d898d50f3fe68bfbf95155.zip |
Rename batch into scanner
Diffstat (limited to 'sonar-scanner-engine/src/test/java')
175 files changed, 21341 insertions, 0 deletions
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/DefaultFileLinesContextTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/DefaultFileLinesContextTest.java new file mode 100644 index 00000000000..eb622b003fa --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/DefaultFileLinesContextTest.java @@ -0,0 +1,148 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Matchers; +import org.sonar.api.batch.SonarIndex; +import org.sonar.api.measures.Measure; +import org.sonar.api.measures.Metric; +import org.sonar.api.measures.PersistenceMode; +import org.sonar.api.resources.Directory; +import org.sonar.api.resources.Resource; +import org.sonar.api.resources.Scopes; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.nullValue; +import static org.junit.Assert.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class DefaultFileLinesContextTest { + + private SonarIndex index; + private Resource resource; + private DefaultFileLinesContext fileLineMeasures; + + @Before + public void setUp() { + index = mock(SonarIndex.class); + resource = mock(Resource.class); + when(resource.getScope()).thenReturn(Scopes.FILE); + fileLineMeasures = new DefaultFileLinesContext(index, resource); + } + + @Test(expected = IllegalArgumentException.class) + public void shouldNotAllowCreationForDirectory() { + new DefaultFileLinesContext(index, Directory.create("key")); + } + + @Test + public void shouldSave() { + fileLineMeasures.setIntValue("hits", 1, 2); + fileLineMeasures.setIntValue("hits", 3, 4); + fileLineMeasures.save(); + + assertThat(fileLineMeasures.toString()).isEqualTo("DefaultFileLinesContext{map={hits={1=2, 3=4}}}"); + + ArgumentCaptor<Measure> measureCaptor = ArgumentCaptor.forClass(Measure.class); + verify(index).addMeasure(Matchers.eq(resource), measureCaptor.capture()); + Measure measure = measureCaptor.getValue(); + assertThat(measure.getMetricKey(), is("hits")); + assertThat(measure.getPersistenceMode(), is(PersistenceMode.DATABASE)); + assertThat(measure.getData(), is("1=2;3=4")); + } + + @Test + public void shouldSaveSeveral() { + fileLineMeasures.setIntValue("hits", 1, 2); + fileLineMeasures.setIntValue("hits", 3, 4); + fileLineMeasures.setStringValue("author", 1, "simon"); + fileLineMeasures.setStringValue("author", 3, "evgeny"); + fileLineMeasures.save(); + fileLineMeasures.setIntValue("branches", 1, 2); + fileLineMeasures.setIntValue("branches", 3, 4); + fileLineMeasures.save(); + + verify(index, times(3)).addMeasure(Matchers.eq(resource), Matchers.any(Measure.class)); + } + + @Test(expected = UnsupportedOperationException.class) + public void shouldNotModifyAfterSave() { + fileLineMeasures.setIntValue("hits", 1, 2); + fileLineMeasures.save(); + fileLineMeasures.save(); + verify(index).addMeasure(Matchers.eq(resource), Matchers.any(Measure.class)); + fileLineMeasures.setIntValue("hits", 1, 2); + } + + @Test + public void shouldLoadIntValues() { + when(index.getMeasure(Matchers.any(Resource.class), Matchers.any(Metric.class))) + .thenReturn(new Measure("hits").setData("1=2;3=4")); + + assertThat(fileLineMeasures.getIntValue("hits", 1), is(2)); + assertThat(fileLineMeasures.getIntValue("hits", 3), is(4)); + assertThat("no measure on line", fileLineMeasures.getIntValue("hits", 5), nullValue()); + } + + @Test + public void shouldLoadStringValues() { + when(index.getMeasure(Matchers.any(Resource.class), Matchers.any(Metric.class))) + .thenReturn(new Measure("author").setData("1=simon;3=evgeny")); + + assertThat(fileLineMeasures.getStringValue("author", 1), is("simon")); + assertThat(fileLineMeasures.getStringValue("author", 3), is("evgeny")); + assertThat("no measure on line", fileLineMeasures.getStringValue("author", 5), nullValue()); + } + + @Test + public void shouldNotSaveAfterLoad() { + when(index.getMeasure(Matchers.any(Resource.class), Matchers.any(Metric.class))) + .thenReturn(new Measure("author").setData("1=simon;3=evgeny")); + + fileLineMeasures.getStringValue("author", 1); + fileLineMeasures.save(); + + verify(index, never()).addMeasure(Matchers.eq(resource), Matchers.any(Measure.class)); + } + + @Test(expected = UnsupportedOperationException.class) + public void shouldNotModifyAfterLoad() { + when(index.getMeasure(Matchers.any(Resource.class), Matchers.any(Metric.class))) + .thenReturn(new Measure("author").setData("1=simon;3=evgeny")); + + fileLineMeasures.getStringValue("author", 1); + fileLineMeasures.setStringValue("author", 1, "evgeny"); + } + + @Test + public void shouldNotFailIfNoMeasureInIndex() { + assertThat(fileLineMeasures.getIntValue("hits", 1), nullValue()); + assertThat(fileLineMeasures.getStringValue("author", 1), nullValue()); + } + +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/ProjectConfiguratorTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/ProjectConfiguratorTest.java new file mode 100644 index 00000000000..6a30188454c --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/ProjectConfiguratorTest.java @@ -0,0 +1,130 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch; + +import java.text.SimpleDateFormat; +import java.util.TimeZone; +import org.junit.Before; +import org.junit.Test; +import org.sonar.api.CoreProperties; +import org.sonar.api.config.Settings; +import org.sonar.api.resources.Project; +import org.sonar.api.utils.System2; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class ProjectConfiguratorTest { + + System2 system2; + + @Before + public void setUp() { + system2 = mock(System2.class); + } + + @Test + public void analysis_is_today_by_default() { + Long now = System.currentTimeMillis(); + when(system2.now()).thenReturn(now); + + Project project = new Project("key"); + new ProjectConfigurator(new Settings(), system2).configure(project); + assertThat(now - project.getAnalysisDate().getTime()).isLessThan(1000); + } + + @Test + public void analysis_date_could_be_explicitly_set() { + Settings settings = new Settings(); + settings.setProperty(CoreProperties.PROJECT_DATE_PROPERTY, "2005-01-30"); + Project project = new Project("key"); + new ProjectConfigurator(settings, system2).configure(project); + + assertThat(new SimpleDateFormat("ddMMyyyy").format(project.getAnalysisDate())).isEqualTo("30012005"); + } + + @Test + public void analysis_timestamp_could_be_explicitly_set() { + Settings settings = new Settings(); + settings.setProperty(CoreProperties.PROJECT_DATE_PROPERTY, "2005-01-30T08:45:10+0000"); + Project project = new Project("key"); + new ProjectConfigurator(settings, system2).configure(project); + + SimpleDateFormat dateFormat = new SimpleDateFormat("ddMMyyyy-mmss"); + dateFormat.setTimeZone(TimeZone.getTimeZone("GMT")); + assertThat(dateFormat.format(project.getAnalysisDate())).isEqualTo("30012005-4510"); + } + + @Test(expected = RuntimeException.class) + public void fail_if_analyis_date_is_not_valid() { + Settings configuration = new Settings(); + configuration.setProperty(CoreProperties.PROJECT_DATE_PROPERTY, "2005/30/01"); + Project project = new Project("key"); + new ProjectConfigurator(configuration, system2).configure(project); + } + + @Test + public void default_analysis_type_is_dynamic() { + Project project = new Project("key"); + new ProjectConfigurator(new Settings(), system2).configure(project); + assertThat(project.getAnalysisType()).isEqualTo(Project.AnalysisType.DYNAMIC); + } + + @Test + public void explicit_dynamic_analysis() { + Settings configuration = new Settings(); + configuration.setProperty(CoreProperties.DYNAMIC_ANALYSIS_PROPERTY, "true"); + Project project = new Project("key"); + new ProjectConfigurator(configuration, system2).configure(project); + assertThat(project.getAnalysisType()).isEqualTo(Project.AnalysisType.DYNAMIC); + } + + @Test + public void explicit_static_analysis() { + Settings configuration = new Settings(); + configuration.setProperty(CoreProperties.DYNAMIC_ANALYSIS_PROPERTY, "false"); + Project project = new Project("key"); + new ProjectConfigurator(configuration, system2).configure(project); + assertThat(project.getAnalysisType()).isEqualTo(Project.AnalysisType.STATIC); + } + + @Test + public void explicit_dynamic_analysis_reusing_reports() { + Settings configuration = new Settings(); + configuration.setProperty(CoreProperties.DYNAMIC_ANALYSIS_PROPERTY, "reuseReports"); + Project project = new Project("key"); + new ProjectConfigurator(configuration, system2).configure(project); + assertThat(project.getAnalysisType()).isEqualTo(Project.AnalysisType.REUSE_REPORTS); + } + + @Test + public void is_dynamic_analysis() { + assertThat(Project.AnalysisType.DYNAMIC.isDynamic(false)).isTrue(); + assertThat(Project.AnalysisType.DYNAMIC.isDynamic(true)).isTrue(); + + assertThat(Project.AnalysisType.STATIC.isDynamic(false)).isFalse(); + assertThat(Project.AnalysisType.STATIC.isDynamic(true)).isFalse(); + + assertThat(Project.AnalysisType.REUSE_REPORTS.isDynamic(false)).isFalse(); + assertThat(Project.AnalysisType.REUSE_REPORTS.isDynamic(true)).isTrue(); + } + +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/analysis/AnalysisTempFolderProviderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/analysis/AnalysisTempFolderProviderTest.java new file mode 100644 index 00000000000..058ea4fd34c --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/analysis/AnalysisTempFolderProviderTest.java @@ -0,0 +1,66 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.analysis; + +import org.sonar.api.batch.bootstrap.ProjectDefinition; + +import org.sonar.api.batch.bootstrap.ProjectReactor; +import org.junit.Before; +import org.sonar.batch.analysis.AnalysisTempFolderProvider; +import org.sonar.api.utils.TempFolder; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +import java.io.File; +import java.io.IOException; + +import static org.mockito.Mockito.when; +import static org.mockito.Mockito.mock; +import static org.assertj.core.api.Assertions.assertThat; + +public class AnalysisTempFolderProviderTest { + + @Rule + public TemporaryFolder temp = new TemporaryFolder(); + + private AnalysisTempFolderProvider tempFolderProvider; + private ProjectReactor projectReactor; + + @Before + public void setUp() { + tempFolderProvider = new AnalysisTempFolderProvider(); + projectReactor = mock(ProjectReactor.class); + ProjectDefinition projectDefinition = mock(ProjectDefinition.class); + when(projectReactor.getRoot()).thenReturn(projectDefinition); + when(projectDefinition.getWorkDir()).thenReturn(temp.getRoot()); + } + + @Test + public void createTempFolder() throws IOException { + File defaultDir = new File(temp.getRoot(), AnalysisTempFolderProvider.TMP_NAME); + + TempFolder tempFolder = tempFolderProvider.provide(projectReactor); + tempFolder.newDir(); + tempFolder.newFile(); + assertThat(defaultDir).exists(); + assertThat(defaultDir.list()).hasSize(2); + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/analysis/AnalysisWSLoaderProviderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/analysis/AnalysisWSLoaderProviderTest.java new file mode 100644 index 00000000000..66ab296a063 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/analysis/AnalysisWSLoaderProviderTest.java @@ -0,0 +1,62 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.analysis; + +import com.google.common.collect.ImmutableMap; +import org.assertj.core.util.Maps; +import org.junit.Test; +import org.sonar.api.batch.AnalysisMode; +import org.sonar.batch.bootstrap.BatchWsClient; +import org.sonar.batch.cache.WSLoader; +import org.sonar.batch.cache.WSLoader.LoadStrategy; +import org.sonar.home.cache.PersistentCache; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class AnalysisWSLoaderProviderTest { + + PersistentCache cache = mock(PersistentCache.class); + BatchWsClient wsClient = mock(BatchWsClient.class); + AnalysisMode mode = mock(AnalysisMode.class); + + AnalysisWSLoaderProvider underTest = new AnalysisWSLoaderProvider(); + + @Test + public void testDefault() { + WSLoader loader = underTest.provide(mode, cache, wsClient, new AnalysisProperties(Maps.<String, String>newHashMap())); + assertThat(loader.getDefaultStrategy()).isEqualTo(LoadStrategy.SERVER_ONLY); + } + + @Test + public void no_cache_by_default_in_issues_mode() { + when(mode.isIssues()).thenReturn(true); + WSLoader loader = underTest.provide(mode, cache, wsClient, new AnalysisProperties(Maps.<String, String>newHashMap())); + assertThat(loader.getDefaultStrategy()).isEqualTo(LoadStrategy.SERVER_ONLY); + } + + @Test + public void enable_cache_in_issues_mode() { + when(mode.isIssues()).thenReturn(true); + WSLoader loader = underTest.provide(mode, cache, wsClient, new AnalysisProperties(ImmutableMap.of(AnalysisWSLoaderProvider.SONAR_USE_WS_CACHE, "true"))); + assertThat(loader.getDefaultStrategy()).isEqualTo(LoadStrategy.CACHE_ONLY); + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/analysis/DefaultAnalysisModeTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/analysis/DefaultAnalysisModeTest.java new file mode 100644 index 00000000000..67734ed62fa --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/analysis/DefaultAnalysisModeTest.java @@ -0,0 +1,141 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.analysis; + +import org.junit.Rule; +import org.junit.rules.ExpectedException; +import org.sonar.batch.analysis.DefaultAnalysisMode; +import org.sonar.batch.analysis.AnalysisProperties; + +import javax.annotation.Nullable; + +import org.sonar.batch.bootstrap.GlobalProperties; +import org.junit.Test; +import org.sonar.api.CoreProperties; + +import java.util.HashMap; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; + +public class DefaultAnalysisModeTest { + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Test + public void regular_analysis_by_default() { + DefaultAnalysisMode mode = createMode(null, null); + assertThat(mode.isPreview()).isFalse(); + assertThat(mode.isPublish()).isTrue(); + } + + @Test(expected = IllegalStateException.class) + public void fail_if_inconsistent() { + createMode(null, CoreProperties.ANALYSIS_MODE_ISSUES); + } + + @Test + public void support_publish_mode() { + DefaultAnalysisMode mode = createMode(CoreProperties.ANALYSIS_MODE_PUBLISH); + + assertThat(mode.isPreview()).isFalse(); + assertThat(mode.isPublish()).isTrue(); + } + + @Test + public void incremental_mode_no_longer_valid() { + thrown.expect(IllegalStateException.class); + thrown.expectMessage("This mode was removed in SonarQube 5.2"); + + createMode(CoreProperties.ANALYSIS_MODE_INCREMENTAL); + } + + @Test + public void invalidate_mode() { + thrown.expect(IllegalStateException.class); + thrown.expectMessage("[preview, publish, issues]"); + + createMode("invalid"); + } + + @Test + public void preview_mode_fallback_issues() { + DefaultAnalysisMode mode = createMode(CoreProperties.ANALYSIS_MODE_PREVIEW); + + assertThat(mode.isIssues()).isTrue(); + assertThat(mode.isPreview()).isFalse(); + } + + @Test + public void scan_all() { + Map<String, String> props = new HashMap<>(); + props.put(CoreProperties.ANALYSIS_MODE, CoreProperties.ANALYSIS_MODE_ISSUES); + GlobalProperties globalProps = new GlobalProperties(props); + + AnalysisProperties analysisProps = new AnalysisProperties(new HashMap<String, String>()); + DefaultAnalysisMode mode = new DefaultAnalysisMode(globalProps, analysisProps); + assertThat(mode.scanAllFiles()).isFalse(); + + props.put("sonar.scanAllFiles", "true"); + analysisProps = new AnalysisProperties(props); + + mode = new DefaultAnalysisMode(globalProps, analysisProps); + assertThat(mode.scanAllFiles()).isTrue(); + + props.put(CoreProperties.ANALYSIS_MODE, CoreProperties.ANALYSIS_MODE_PUBLISH); + analysisProps = new AnalysisProperties(props); + + mode = new DefaultAnalysisMode(globalProps, analysisProps); + assertThat(mode.scanAllFiles()).isTrue(); + } + + @Test + public void default_publish_mode() { + DefaultAnalysisMode mode = createMode(null); + assertThat(mode.isPublish()).isTrue(); + assertThat(mode.scanAllFiles()).isTrue(); + } + + @Test + public void support_issues_mode() { + DefaultAnalysisMode mode = createMode(CoreProperties.ANALYSIS_MODE_ISSUES); + + assertThat(mode.isIssues()).isTrue(); + assertThat(mode.scanAllFiles()).isFalse(); + } + + private static DefaultAnalysisMode createMode(@Nullable String mode) { + return createMode(mode, mode); + } + + private static DefaultAnalysisMode createMode(@Nullable String bootstrapMode, @Nullable String analysisMode) { + Map<String, String> bootstrapMap = new HashMap<>(); + Map<String, String> analysisMap = new HashMap<>(); + + if (bootstrapMode != null) { + bootstrapMap.put(CoreProperties.ANALYSIS_MODE, bootstrapMode); + } + if (analysisMode != null) { + analysisMap.put(CoreProperties.ANALYSIS_MODE, analysisMode); + } + return new DefaultAnalysisMode(new GlobalProperties(bootstrapMap), new AnalysisProperties(analysisMap)); + } + +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/BatchExtensionDictionnaryTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/BatchExtensionDictionnaryTest.java new file mode 100644 index 00000000000..77a0021ef31 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/BatchExtensionDictionnaryTest.java @@ -0,0 +1,412 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.bootstrap; + +import com.google.common.collect.Lists; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import org.junit.Test; +import org.sonar.api.BatchExtension; +import org.sonar.api.batch.BuildBreaker; +import org.sonar.api.batch.CheckProject; +import org.sonar.api.batch.Decorator; +import org.sonar.api.batch.DependedUpon; +import org.sonar.api.batch.DependsUpon; +import org.sonar.api.batch.Phase; +import org.sonar.api.batch.PostJob; +import org.sonar.api.batch.Sensor; +import org.sonar.api.batch.SensorContext; +import org.sonar.api.batch.postjob.PostJobContext; +import org.sonar.api.resources.Project; +import org.sonar.batch.postjob.PostJobOptimizer; +import org.sonar.batch.sensor.DefaultSensorContext; +import org.sonar.batch.sensor.SensorOptimizer; +import org.sonar.core.platform.ComponentContainer; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.mock; + +public class BatchExtensionDictionnaryTest { + + private BatchExtensionDictionnary newSelector(Object... extensions) { + ComponentContainer iocContainer = new ComponentContainer(); + for (Object extension : extensions) { + iocContainer.addSingleton(extension); + } + return new BatchExtensionDictionnary(iocContainer, mock(DefaultSensorContext.class), mock(SensorOptimizer.class), mock(PostJobContext.class), + mock(PostJobOptimizer.class)); + } + + @Test + public void testGetFilteredExtensionWithExtensionMatcher() { + final Sensor sensor1 = new FakeSensor(); + final Sensor sensor2 = new FakeSensor(); + + BatchExtensionDictionnary selector = newSelector(sensor1, sensor2); + Collection<Sensor> sensors = selector.select(Sensor.class, null, true, new ExtensionMatcher() { + @Override + public boolean accept(Object extension) { + return extension.equals(sensor1); + } + }); + + assertThat(sensors).contains(sensor1); + assertEquals(1, sensors.size()); + } + + @Test + public void testGetFilteredExtensions() { + Sensor sensor1 = new FakeSensor(); + Sensor sensor2 = new FakeSensor(); + Decorator decorator = mock(Decorator.class); + + BatchExtensionDictionnary selector = newSelector(sensor1, sensor2, decorator); + Collection<Sensor> sensors = selector.select(Sensor.class, null, true, null); + + assertThat(sensors).containsOnly(sensor1, sensor2); + } + + @Test + public void shouldSearchInParentContainers() { + Sensor a = new FakeSensor(); + Sensor b = new FakeSensor(); + Sensor c = new FakeSensor(); + + ComponentContainer grandParent = new ComponentContainer(); + grandParent.addSingleton(a); + + ComponentContainer parent = grandParent.createChild(); + parent.addSingleton(b); + + ComponentContainer child = parent.createChild(); + child.addSingleton(c); + + BatchExtensionDictionnary dictionnary = new BatchExtensionDictionnary(child, mock(DefaultSensorContext.class), mock(SensorOptimizer.class), mock(PostJobContext.class), + mock(PostJobOptimizer.class)); + assertThat(dictionnary.select(Sensor.class, null, true, null)).containsOnly(a, b, c); + } + + @Test + public void sortExtensionsByDependency() { + BatchExtension a = new MethodDependentOf(null); + BatchExtension b = new MethodDependentOf(a); + BatchExtension c = new MethodDependentOf(b); + + BatchExtensionDictionnary selector = newSelector(b, c, a); + List<BatchExtension> extensions = Lists.newArrayList(selector.select(BatchExtension.class, null, true, null)); + + assertThat(extensions).hasSize(3); + assertThat(extensions.get(0)).isEqualTo(a); + assertThat(extensions.get(1)).isEqualTo(b); + assertThat(extensions.get(2)).isEqualTo(c); + } + + @Test + public void useMethodAnnotationsToSortExtensions() { + BatchExtension a = new GeneratesSomething("foo"); + BatchExtension b = new MethodDependentOf("foo"); + + BatchExtensionDictionnary selector = newSelector(a, b); + List<BatchExtension> extensions = Lists.newArrayList(selector.select(BatchExtension.class, null, true, null)); + + assertThat(extensions.size()).isEqualTo(2); + assertThat(extensions.get(0)).isEqualTo(a); + assertThat(extensions.get(1)).isEqualTo(b); + + // different initial order + selector = newSelector(b, a); + extensions = Lists.newArrayList(selector.select(BatchExtension.class, null, true, null)); + + assertThat(extensions).hasSize(2); + assertThat(extensions.get(0)).isEqualTo(a); + assertThat(extensions.get(1)).isEqualTo(b); + } + + @Test + public void methodDependsUponCollection() { + BatchExtension a = new GeneratesSomething("foo"); + BatchExtension b = new MethodDependentOf(Arrays.asList("foo")); + + BatchExtensionDictionnary selector = newSelector(a, b); + List<BatchExtension> extensions = Lists.newArrayList(selector.select(BatchExtension.class, null, true, null)); + + assertThat(extensions).hasSize(2); + assertThat(extensions.get(0)).isEqualTo(a); + assertThat(extensions.get(1)).isEqualTo(b); + + // different initial order + selector = newSelector(b, a); + extensions = Lists.newArrayList(selector.select(BatchExtension.class, null, true, null)); + + assertThat(extensions).hasSize(2); + assertThat(extensions.get(0)).isEqualTo(a); + assertThat(extensions.get(1)).isEqualTo(b); + } + + @Test + public void methodDependsUponArray() { + BatchExtension a = new GeneratesSomething("foo"); + BatchExtension b = new MethodDependentOf(new String[] {"foo"}); + + BatchExtensionDictionnary selector = newSelector(a, b); + List<BatchExtension> extensions = Lists.newArrayList(selector.select(BatchExtension.class, null, true, null)); + + assertThat(extensions).hasSize(2); + assertThat(extensions.get(0)).isEqualTo(a); + assertThat(extensions.get(1)).isEqualTo(b); + + // different initial order + selector = newSelector(b, a); + extensions = Lists.newArrayList(selector.select(BatchExtension.class, null, true, null)); + + assertThat(extensions).hasSize(2); + assertThat(extensions.get(0)).isEqualTo(a); + assertThat(extensions.get(1)).isEqualTo(b); + } + + @Test + public void useClassAnnotationsToSortExtensions() { + BatchExtension a = new ClassDependedUpon(); + BatchExtension b = new ClassDependsUpon(); + + BatchExtensionDictionnary selector = newSelector(a, b); + List<BatchExtension> extensions = Lists.newArrayList(selector.select(BatchExtension.class, null, true, null)); + + assertThat(extensions).hasSize(2); + assertThat(extensions.get(0)).isEqualTo(a); + assertThat(extensions.get(1)).isEqualTo(b); + + // different initial order + selector = newSelector(b, a); + extensions = Lists.newArrayList(selector.select(BatchExtension.class, null, true, null)); + + assertThat(extensions).hasSize(2); + assertThat(extensions.get(0)).isEqualTo(a); + assertThat(extensions.get(1)).isEqualTo(b); + } + + @Test + public void useClassAnnotationsOnInterfaces() { + BatchExtension a = new InterfaceDependedUpon() { + }; + BatchExtension b = new InterfaceDependsUpon() { + }; + + BatchExtensionDictionnary selector = newSelector(a, b); + List<BatchExtension> extensions = Lists.newArrayList(selector.select(BatchExtension.class, null, true, null)); + + assertThat(extensions).hasSize(2); + assertThat(extensions.get(0)).isEqualTo(a); + assertThat(extensions.get(1)).isEqualTo(b); + + // different initial order + selector = newSelector(b, a); + extensions = Lists.newArrayList(selector.select(BatchExtension.class, null, true, null)); + + assertThat(extensions).hasSize(2); + assertThat(extensions.get(0)).isEqualTo(a); + assertThat(extensions.get(1)).isEqualTo(b); + } + + @Test + public void checkProject() { + BatchExtension ok = new CheckProjectOK(); + BatchExtension ko = new CheckProjectKO(); + + BatchExtensionDictionnary selector = newSelector(ok, ko); + List<BatchExtension> extensions = Lists.newArrayList(selector.select(BatchExtension.class, new Project("key"), true, null)); + + assertThat(extensions).hasSize(1); + assertThat(extensions.get(0)).isInstanceOf(CheckProjectOK.class); + } + + @Test + public void inheritAnnotations() { + BatchExtension a = new SubClass("foo"); + BatchExtension b = new MethodDependentOf("foo"); + + BatchExtensionDictionnary selector = newSelector(b, a); + List<BatchExtension> extensions = Lists.newArrayList(selector.select(BatchExtension.class, null, true, null)); + + assertThat(extensions).hasSize(2); + assertThat(extensions.get(0)).isEqualTo(a); + assertThat(extensions.get(1)).isEqualTo(b); + + // change initial order + selector = newSelector(a, b); + extensions = Lists.newArrayList(selector.select(BatchExtension.class, null, true, null)); + + assertThat(extensions).hasSize(2); + assertThat(extensions.get(0)).isEqualTo(a); + assertThat(extensions.get(1)).isEqualTo(b); + } + + @Test(expected = IllegalStateException.class) + public void annotatedMethodsCanNotBePrivate() { + BatchExtensionDictionnary selector = newSelector(); + BatchExtension wrong = new BatchExtension() { + @DependsUpon + private Object foo() { + return "foo"; + } + }; + selector.evaluateAnnotatedClasses(wrong, DependsUpon.class); + } + + @Test + public void dependsUponPhase() { + BatchExtension pre = new PreSensor(); + BatchExtension analyze = new GeneratesSomething("something"); + BatchExtension post = new PostSensor(); + + BatchExtensionDictionnary selector = newSelector(analyze, post, pre); + List extensions = Lists.newArrayList(selector.select(BatchExtension.class, null, true, null)); + + assertThat(extensions).hasSize(3); + assertThat(extensions.get(0)).isEqualTo(pre); + assertThat(extensions.get(1)).isEqualTo(analyze); + assertThat(extensions.get(2)).isEqualTo(post); + } + + @Test + public void dependsUponInheritedPhase() { + BatchExtension pre = new PreSensorSubclass(); + BatchExtension analyze = new GeneratesSomething("something"); + BatchExtension post = new PostSensorSubclass(); + + BatchExtensionDictionnary selector = newSelector(analyze, post, pre); + List extensions = Lists.newArrayList(selector.select(BatchExtension.class, null, true, null)); + + assertThat(extensions).hasSize(3); + assertThat(extensions.get(0)).isEqualTo(pre); + assertThat(extensions.get(1)).isEqualTo(analyze); + assertThat(extensions.get(2)).isEqualTo(post); + } + + @Test + public void buildStatusCheckersAreExecutedAfterOtherPostJobs() { + BuildBreaker checker = new BuildBreaker() { + public void executeOn(Project project, SensorContext context) { + } + }; + + BatchExtensionDictionnary selector = newSelector(new FakePostJob(), checker, new FakePostJob()); + List extensions = Lists.newArrayList(selector.select(PostJob.class, null, true, null)); + + assertThat(extensions).hasSize(3); + assertThat(extensions.get(2)).isEqualTo(checker); + } + + class FakeSensor implements Sensor { + + public void analyse(Project project, SensorContext context) { + + } + + public boolean shouldExecuteOnProject(Project project) { + return true; + } + } + + class MethodDependentOf implements BatchExtension { + private Object dep; + + MethodDependentOf(Object o) { + this.dep = o; + } + + @DependsUpon + public Object dependsUponObject() { + return dep; + } + } + + @DependsUpon("flag") + class ClassDependsUpon implements BatchExtension { + } + + @DependedUpon("flag") + class ClassDependedUpon implements BatchExtension { + } + + @DependsUpon("flag") + interface InterfaceDependsUpon extends BatchExtension { + } + + @DependedUpon("flag") + interface InterfaceDependedUpon extends BatchExtension { + } + + class GeneratesSomething implements BatchExtension { + private Object gen; + + GeneratesSomething(Object o) { + this.gen = o; + } + + @DependedUpon + public Object generates() { + return gen; + } + } + + class SubClass extends GeneratesSomething { + SubClass(Object o) { + super(o); + } + } + + @Phase(name = Phase.Name.PRE) + class PreSensor implements BatchExtension { + + } + + class PreSensorSubclass extends PreSensor { + + } + + @Phase(name = Phase.Name.POST) + class PostSensor implements BatchExtension { + + } + + class PostSensorSubclass extends PostSensor { + + } + + class CheckProjectOK implements BatchExtension, CheckProject { + public boolean shouldExecuteOnProject(Project project) { + return true; + } + } + + class CheckProjectKO implements BatchExtension, CheckProject { + public boolean shouldExecuteOnProject(Project project) { + return false; + } + } + + private class FakePostJob implements PostJob { + public void executeOn(Project project, SensorContext context) { + } + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/BatchPluginInstallerTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/BatchPluginInstallerTest.java new file mode 100644 index 00000000000..95e17ca849a --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/BatchPluginInstallerTest.java @@ -0,0 +1,86 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.bootstrap; + +import java.io.File; +import java.util.List; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.rules.TemporaryFolder; +import org.sonar.batch.cache.WSLoader; +import org.sonar.batch.cache.WSLoaderResult; +import org.sonar.core.platform.RemotePlugin; +import org.sonar.home.cache.FileCache; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class BatchPluginInstallerTest { + + @Rule + public TemporaryFolder temp = new TemporaryFolder(); + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + FileCache fileCache = mock(FileCache.class); + BatchWsClient wsClient = mock(BatchWsClient.class); + BatchPluginPredicate pluginPredicate = mock(BatchPluginPredicate.class); + + @Test + public void listRemotePlugins() { + + WSLoader wsLoader = mock(WSLoader.class); + when(wsLoader.loadString("/deploy/plugins/index.txt")).thenReturn(new WSLoaderResult<>("checkstyle\nsqale", true)); + BatchPluginInstaller underTest = new BatchPluginInstaller(wsLoader, wsClient, fileCache, pluginPredicate); + + List<RemotePlugin> remotePlugins = underTest.listRemotePlugins(); + assertThat(remotePlugins).extracting("key").containsOnly("checkstyle", "sqale"); + } + + @Test + public void should_download_plugin() throws Exception { + File pluginJar = temp.newFile(); + when(fileCache.get(eq("checkstyle-plugin.jar"), eq("fakemd5_1"), any(FileCache.Downloader.class))).thenReturn(pluginJar); + + WSLoader wsLoader = mock(WSLoader.class); + BatchPluginInstaller underTest = new BatchPluginInstaller(wsLoader, wsClient, fileCache, pluginPredicate); + + RemotePlugin remote = new RemotePlugin("checkstyle").setFile("checkstyle-plugin.jar", "fakemd5_1"); + File file = underTest.download(remote); + + assertThat(file).isEqualTo(pluginJar); + } + + @Test + public void should_fail_to_get_plugin_index() { + thrown.expect(IllegalStateException.class); + + WSLoader wsLoader = mock(WSLoader.class); + doThrow(new IllegalStateException()).when(wsLoader).loadString("/deploy/plugins/index.txt"); + + new BatchPluginInstaller(wsLoader, wsClient, fileCache, pluginPredicate).installRemotes(); + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/BatchPluginJarExploderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/BatchPluginJarExploderTest.java new file mode 100644 index 00000000000..fe991f5d406 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/BatchPluginJarExploderTest.java @@ -0,0 +1,80 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.bootstrap; + +import java.io.File; +import java.io.IOException; +import org.apache.commons.io.FileUtils; +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.sonar.core.platform.ExplodedPlugin; +import org.sonar.core.platform.PluginInfo; +import org.sonar.home.cache.FileCache; +import org.sonar.home.cache.FileCacheBuilder; + +import static org.assertj.core.api.Assertions.assertThat; + +public class BatchPluginJarExploderTest { + + @ClassRule + public static TemporaryFolder temp = new TemporaryFolder(); + + File userHome; + BatchPluginJarExploder underTest; + + @Before + public void setUp() throws IOException { + userHome = temp.newFolder(); + FileCache fileCache = new FileCacheBuilder(new Slf4jLogger()).setUserHome(userHome).build(); + underTest = new BatchPluginJarExploder(fileCache); + } + + @Test + public void copy_and_extract_libs() throws IOException { + File fileFromCache = getFileFromCache("sonar-checkstyle-plugin-2.8.jar"); + ExplodedPlugin exploded = underTest.explode(PluginInfo.create(fileFromCache)); + + assertThat(exploded.getKey()).isEqualTo("checkstyle"); + assertThat(exploded.getMain()).isFile().exists(); + assertThat(exploded.getLibs()).extracting("name").containsOnly("antlr-2.7.6.jar", "checkstyle-5.1.jar", "commons-cli-1.0.jar"); + assertThat(new File(fileFromCache.getParent(), "sonar-checkstyle-plugin-2.8.jar")).exists(); + assertThat(new File(fileFromCache.getParent(), "sonar-checkstyle-plugin-2.8.jar_unzip/META-INF/lib/checkstyle-5.1.jar")).exists(); + } + + @Test + public void extract_only_libs() throws IOException { + File fileFromCache = getFileFromCache("sonar-checkstyle-plugin-2.8.jar"); + underTest.explode(PluginInfo.create(fileFromCache)); + + assertThat(new File(fileFromCache.getParent(), "sonar-checkstyle-plugin-2.8.jar")).exists(); + assertThat(new File(fileFromCache.getParent(), "sonar-checkstyle-plugin-2.8.jar_unzip/META-INF/MANIFEST.MF")).doesNotExist(); + assertThat(new File(fileFromCache.getParent(), "sonar-checkstyle-plugin-2.8.jar_unzip/org/sonar/plugins/checkstyle/CheckstyleVersion.class")).doesNotExist(); + } + + File getFileFromCache(String filename) throws IOException { + File src = FileUtils.toFile(getClass().getResource(this.getClass().getSimpleName() + "/" + filename)); + File destFile = new File(new File(userHome, "" + filename.hashCode()), filename); + FileUtils.copyFile(src, destFile); + return destFile; + } + +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/BatchPluginPredicateTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/BatchPluginPredicateTest.java new file mode 100644 index 00000000000..0e8f29a1609 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/BatchPluginPredicateTest.java @@ -0,0 +1,103 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.bootstrap; + +import org.junit.Test; +import org.sonar.api.CoreProperties; +import org.sonar.api.config.Settings; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class BatchPluginPredicateTest { + + Settings settings = new Settings(); + GlobalMode mode = mock(GlobalMode.class); + + @Test + public void accept_if_no_inclusions_nor_exclusions() { + BatchPluginPredicate predicate = new BatchPluginPredicate(settings, mode); + assertThat(predicate.getWhites()).isEmpty(); + assertThat(predicate.getBlacks()).isEmpty(); + assertThat(predicate.apply("pmd")).isTrue(); + assertThat(predicate.apply("buildbreaker")).isTrue(); + } + + @Test + public void exclude_buildbreaker_in_preview_mode() { + when(mode.isPreview()).thenReturn(true); + BatchPluginPredicate predicate = new BatchPluginPredicate(settings, mode); + assertThat(predicate.apply("buildbreaker")).isFalse(); + } + + @Test + public void inclusions_take_precedence_over_exclusions() { + when(mode.isPreview()).thenReturn(true); + settings + .setProperty(CoreProperties.PREVIEW_INCLUDE_PLUGINS, "checkstyle,pmd,findbugs") + .setProperty(CoreProperties.PREVIEW_EXCLUDE_PLUGINS, "cobertura,pmd"); + BatchPluginPredicate predicate = new BatchPluginPredicate(settings, mode); + assertThat(predicate.apply("pmd")).isTrue(); + } + + @Test + public void verify_both_inclusions_and_exclusions() { + when(mode.isPreview()).thenReturn(true); + settings + .setProperty(CoreProperties.PREVIEW_INCLUDE_PLUGINS, "checkstyle,pmd,findbugs") + .setProperty(CoreProperties.PREVIEW_EXCLUDE_PLUGINS, "cobertura"); + BatchPluginPredicate predicate = new BatchPluginPredicate(settings, mode); + assertThat(predicate.apply("checkstyle")).isTrue(); + assertThat(predicate.apply("pmd")).isTrue(); + assertThat(predicate.apply("cobertura")).isFalse(); + } + + @Test + public void verify_both_inclusions_and_exclusions_issues() { + when(mode.isIssues()).thenReturn(true); + settings + .setProperty(CoreProperties.PREVIEW_INCLUDE_PLUGINS, "checkstyle,pmd,findbugs") + .setProperty(CoreProperties.PREVIEW_EXCLUDE_PLUGINS, "cobertura"); + BatchPluginPredicate predicate = new BatchPluginPredicate(settings, mode); + assertThat(predicate.apply("checkstyle")).isTrue(); + assertThat(predicate.apply("pmd")).isTrue(); + assertThat(predicate.apply("cobertura")).isFalse(); + } + + @Test + public void test_exclusions_without_any_inclusions() { + when(mode.isPreview()).thenReturn(true); + settings.setProperty(CoreProperties.PREVIEW_EXCLUDE_PLUGINS, "checkstyle,pmd,findbugs"); + BatchPluginPredicate predicate = new BatchPluginPredicate(settings, mode); + assertThat(predicate.apply("checkstyle")).isFalse(); + assertThat(predicate.apply("pmd")).isFalse(); + assertThat(predicate.apply("cobertura")).isTrue(); + } + + @Test + public void trim_inclusions_and_exclusions() { + settings + .setProperty(CoreProperties.PREVIEW_INCLUDE_PLUGINS, "checkstyle, pmd, findbugs") + .setProperty(CoreProperties.PREVIEW_EXCLUDE_PLUGINS, "cobertura, pmd"); + BatchPluginPredicate predicate = new BatchPluginPredicate(settings, mode); + assertThat(predicate.apply("pmd")).isTrue(); + } + +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/BatchPluginRepositoryTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/BatchPluginRepositoryTest.java new file mode 100644 index 00000000000..0d0fc068dfe --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/BatchPluginRepositoryTest.java @@ -0,0 +1,76 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.bootstrap; + +import com.google.common.collect.ImmutableMap; +import org.junit.Test; +import org.sonar.api.SonarPlugin; +import org.sonar.core.platform.PluginInfo; +import org.sonar.core.platform.PluginLoader; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.fail; +import static org.mockito.Matchers.anyCollectionOf; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class BatchPluginRepositoryTest { + + PluginInstaller installer = mock(PluginInstaller.class); + PluginLoader loader = mock(PluginLoader.class); + BatchPluginRepository underTest = new BatchPluginRepository(installer, loader); + + @Test + public void install_and_load_plugins() { + PluginInfo info = new PluginInfo("squid"); + ImmutableMap<String, PluginInfo> infos = ImmutableMap.of("squid", info); + SonarPlugin instance = mock(SonarPlugin.class); + when(loader.load(infos)).thenReturn(ImmutableMap.of("squid", instance)); + when(installer.installRemotes()).thenReturn(infos); + + underTest.start(); + + assertThat(underTest.getPluginInfos()).containsOnly(info); + assertThat(underTest.getPluginInfo("squid")).isSameAs(info); + assertThat(underTest.getPluginInstance("squid")).isSameAs(instance); + + underTest.stop(); + verify(loader).unload(anyCollectionOf(SonarPlugin.class)); + } + + @Test + public void fail_if_requesting_missing_plugin() { + underTest.start(); + + try { + underTest.getPluginInfo("unknown"); + fail(); + } catch (IllegalStateException e) { + assertThat(e).hasMessage("Plugin [unknown] does not exist"); + } + try { + underTest.getPluginInstance("unknown"); + fail(); + } catch (IllegalStateException e) { + assertThat(e).hasMessage("Plugin [unknown] does not exist"); + } + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/BatchWsClientProviderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/BatchWsClientProviderTest.java new file mode 100644 index 00000000000..76fa07167ab --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/BatchWsClientProviderTest.java @@ -0,0 +1,76 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.bootstrap; + +import java.util.HashMap; +import java.util.Map; +import org.junit.Test; +import org.sonar.batch.bootstrapper.EnvironmentInformation; +import org.sonarqube.ws.client.HttpConnector; + +import static org.assertj.core.api.Assertions.assertThat; + +public class BatchWsClientProviderTest { + + BatchWsClientProvider underTest = new BatchWsClientProvider(); + EnvironmentInformation env = new EnvironmentInformation("Maven Plugin", "2.3"); + + @Test + public void provide_client_with_default_settings() { + GlobalProperties settings = new GlobalProperties(new HashMap<String, String>()); + + BatchWsClient client = underTest.provide(settings, env); + + assertThat(client).isNotNull(); + assertThat(client.baseUrl()).isEqualTo("http://localhost:9000/"); + HttpConnector httpConnector = (HttpConnector) client.wsConnector(); + assertThat(httpConnector.baseUrl()).isEqualTo("http://localhost:9000/"); + assertThat(httpConnector.okHttpClient().getProxy()).isNull(); + assertThat(httpConnector.okHttpClient().getConnectTimeout()).isEqualTo(5_000); + assertThat(httpConnector.okHttpClient().getReadTimeout()).isEqualTo(60_000); + assertThat(httpConnector.userAgent()).isEqualTo("Maven Plugin/2.3"); + } + + @Test + public void provide_client_with_custom_settings() { + Map<String, String> props = new HashMap<>(); + props.put("sonar.host.url", "https://here/sonarqube"); + props.put("sonar.login", "theLogin"); + props.put("sonar.password", "thePassword"); + props.put("sonar.ws.timeout", "42"); + GlobalProperties settings = new GlobalProperties(props); + + BatchWsClient client = underTest.provide(settings, env); + + assertThat(client).isNotNull(); + HttpConnector httpConnector = (HttpConnector) client.wsConnector(); + assertThat(httpConnector.baseUrl()).isEqualTo("https://here/sonarqube/"); + assertThat(httpConnector.okHttpClient().getProxy()).isNull(); + assertThat(httpConnector.userAgent()).isEqualTo("Maven Plugin/2.3"); + } + + @Test + public void build_singleton() { + GlobalProperties settings = new GlobalProperties(new HashMap<String, String>()); + BatchWsClient first = underTest.provide(settings, env); + BatchWsClient second = underTest.provide(settings, env); + assertThat(first).isSameAs(second); + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/BatchWsClientTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/BatchWsClientTest.java new file mode 100644 index 00000000000..3689c2d601a --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/BatchWsClientTest.java @@ -0,0 +1,116 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.bootstrap; + +import java.util.List; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.mockito.Mockito; +import org.sonar.api.utils.MessageException; +import org.sonar.api.utils.log.LogTester; +import org.sonar.api.utils.log.LoggerLevel; +import org.sonarqube.ws.client.GetRequest; +import org.sonarqube.ws.client.MockWsResponse; +import org.sonarqube.ws.client.WsClient; +import org.sonarqube.ws.client.WsRequest; +import org.sonarqube.ws.client.WsResponse; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class BatchWsClientTest { + + @Rule + public LogTester logTester = new LogTester(); + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + WsClient wsClient = mock(WsClient.class, Mockito.RETURNS_DEEP_STUBS); + + @Test + public void log_and_profile_request_if_debug_level() throws Exception { + WsRequest request = newRequest(); + WsResponse response = newResponse().setRequestUrl("https://local/api/issues/search"); + when(wsClient.wsConnector().call(request)).thenReturn(response); + + logTester.setLevel(LoggerLevel.DEBUG); + BatchWsClient underTest = new BatchWsClient(wsClient, false); + + WsResponse result = underTest.call(request); + + // do not fail the execution -> interceptor returns the response + assertThat(result).isSameAs(response); + + // check logs + List<String> debugLogs = logTester.logs(LoggerLevel.DEBUG); + assertThat(debugLogs).hasSize(1); + assertThat(debugLogs.get(0)).contains("GET 200 https://local/api/issues/search | time="); + } + + @Test + public void fail_if_requires_credentials() throws Exception { + expectedException.expect(MessageException.class); + expectedException + .expectMessage("Not authorized. Analyzing this project requires to be authenticated. Please provide the values of the properties sonar.login and sonar.password."); + + WsRequest request = newRequest(); + WsResponse response = newResponse().setCode(401); + when(wsClient.wsConnector().call(request)).thenReturn(response); + + new BatchWsClient(wsClient, false).call(request); + } + + @Test + public void fail_if_credentials_are_not_valid() throws Exception { + expectedException.expect(MessageException.class); + expectedException.expectMessage("Not authorized. Please check the properties sonar.login and sonar.password."); + + WsRequest request = newRequest(); + WsResponse response = newResponse().setCode(401); + when(wsClient.wsConnector().call(request)).thenReturn(response); + + new BatchWsClient(wsClient, /* credentials are configured */true).call(request); + } + + @Test + public void fail_if_requires_permission() throws Exception { + expectedException.expect(MessageException.class); + expectedException.expectMessage("missing scan permission, missing another permission"); + + WsRequest request = newRequest(); + WsResponse response = newResponse() + .setCode(403) + .setContent("{\"errors\":[{\"msg\":\"missing scan permission\"}, {\"msg\":\"missing another permission\"}]}"); + when(wsClient.wsConnector().call(request)).thenReturn(response); + + new BatchWsClient(wsClient, true).call(request); + } + + private MockWsResponse newResponse() { + return new MockWsResponse().setRequestUrl("https://local/api/issues/search"); + } + + private WsRequest newRequest() { + return new GetRequest("api/issues/search"); + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/DroppedPropertyCheckerTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/DroppedPropertyCheckerTest.java new file mode 100644 index 00000000000..e6cd758d253 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/DroppedPropertyCheckerTest.java @@ -0,0 +1,62 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.bootstrap; + +import com.google.common.collect.ImmutableMap; +import org.junit.Rule; +import org.junit.Test; +import org.sonar.api.utils.log.LogTester; +import org.sonar.api.utils.log.LoggerLevel; + +import static org.assertj.core.api.Assertions.assertThat; + +public class DroppedPropertyCheckerTest { + private static final String SOME_VALUE = "some value"; + private static final String DROPPED_PROPERTY_1 = "I'm dropped"; + private static final String DROPPED_PROPERTY_MSG_1 = "blablabla!"; + + @Rule + public LogTester logTester = new LogTester(); + + @Test + public void no_log_if_no_dropped_property() { + new DroppedPropertyChecker(ImmutableMap.of(DROPPED_PROPERTY_1, SOME_VALUE), ImmutableMap.<String, String>of()).checkDroppedProperties(); + + assertThat(logTester.logs()).isEmpty(); + } + + @Test + public void no_log_if_settings_does_not_contain_any_dropped_property() { + new DroppedPropertyChecker(ImmutableMap.<String, String>of(), ImmutableMap.of(DROPPED_PROPERTY_1, DROPPED_PROPERTY_MSG_1)).checkDroppedProperties(); + + assertThat(logTester.logs()).isEmpty(); + } + + @Test + public void warn_log_if_settings_contains_any_dropped_property() { + new DroppedPropertyChecker(ImmutableMap.of(DROPPED_PROPERTY_1, SOME_VALUE), ImmutableMap.of(DROPPED_PROPERTY_1, DROPPED_PROPERTY_MSG_1)).checkDroppedProperties(); + + assertThat(logTester.logs(LoggerLevel.ERROR)).isEmpty(); + assertThat(logTester.logs(LoggerLevel.WARN)).containsOnly("Property '" + DROPPED_PROPERTY_1 + "' is not supported any more. " + DROPPED_PROPERTY_MSG_1); + assertThat(logTester.logs(LoggerLevel.INFO)).isEmpty(); + assertThat(logTester.logs(LoggerLevel.DEBUG)).isEmpty(); + assertThat(logTester.logs(LoggerLevel.TRACE)).isEmpty(); + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/ExtensionInstallerTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/ExtensionInstallerTest.java new file mode 100644 index 00000000000..c368fb146b6 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/ExtensionInstallerTest.java @@ -0,0 +1,136 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.bootstrap; + +import java.util.Arrays; +import java.util.List; +import org.apache.commons.lang.ClassUtils; +import org.junit.Before; +import org.junit.Test; +import org.sonar.api.BatchExtension; +import org.sonar.api.ExtensionProvider; +import org.sonar.api.SonarPlugin; +import org.sonar.api.batch.AnalysisMode; +import org.sonar.core.platform.ComponentContainer; +import org.sonar.core.platform.PluginInfo; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class ExtensionInstallerTest { + + GlobalMode mode; + BatchPluginRepository pluginRepository = mock(BatchPluginRepository.class); + + private static SonarPlugin newPluginInstance(final Object... extensions) { + return new SonarPlugin() { + public List getExtensions() { + return Arrays.asList(extensions); + } + }; + } + + @Before + public void setUp() { + mode = mock(GlobalMode.class); + } + + @Test + public void should_filter_extensions_to_install() { + when(pluginRepository.getPluginInfos()).thenReturn(Arrays.asList(new PluginInfo("foo"))); + when(pluginRepository.getPluginInstance("foo")).thenReturn(newPluginInstance(Foo.class, Bar.class)); + + ComponentContainer container = new ComponentContainer(); + ExtensionInstaller installer = new ExtensionInstaller(pluginRepository, mock(AnalysisMode.class)); + installer.install(container, new FooMatcher()); + + assertThat(container.getComponentByType(Foo.class)).isNotNull(); + assertThat(container.getComponentByType(Bar.class)).isNull(); + } + + @Test + public void should_execute_extension_provider() { + when(pluginRepository.getPluginInfos()).thenReturn(Arrays.asList(new PluginInfo("foo"))); + when(pluginRepository.getPluginInstance("foo")).thenReturn(newPluginInstance(new FooProvider(), new BarProvider())); + ComponentContainer container = new ComponentContainer(); + ExtensionInstaller installer = new ExtensionInstaller(pluginRepository, mock(AnalysisMode.class)); + + installer.install(container, new FooMatcher()); + + assertThat(container.getComponentByType(Foo.class)).isNotNull(); + assertThat(container.getComponentByType(Bar.class)).isNull(); + } + + @Test + public void should_provide_list_of_extensions() { + when(pluginRepository.getPluginInfos()).thenReturn(Arrays.asList(new PluginInfo("foo"))); + when(pluginRepository.getPluginInstance("foo")).thenReturn(newPluginInstance(new FooBarProvider())); + ComponentContainer container = new ComponentContainer(); + ExtensionInstaller installer = new ExtensionInstaller(pluginRepository, mock(AnalysisMode.class)); + + installer.install(container, new TrueMatcher()); + + assertThat(container.getComponentByType(Foo.class)).isNotNull(); + assertThat(container.getComponentByType(Bar.class)).isNotNull(); + } + + private static class FooMatcher implements ExtensionMatcher { + public boolean accept(Object extension) { + return extension.equals(Foo.class) || ClassUtils.isAssignable(Foo.class, extension.getClass()) || ClassUtils.isAssignable(FooProvider.class, extension.getClass()); + } + } + + private static class TrueMatcher implements ExtensionMatcher { + public boolean accept(Object extension) { + return true; + } + } + + public static class Foo implements BatchExtension { + + } + + public static class Bar implements BatchExtension { + + } + + public static class FooProvider extends ExtensionProvider implements BatchExtension { + @Override + public Object provide() { + return new Foo(); + } + } + + public static class BarProvider extends ExtensionProvider implements BatchExtension { + @Override + public Object provide() { + return new Bar(); + } + } + + public static class FooBarProvider extends ExtensionProvider implements BatchExtension { + @Override + public Object provide() { + return Arrays.asList(new Foo(), new Bar()); + } + } + +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/ExtensionUtilsTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/ExtensionUtilsTest.java new file mode 100644 index 00000000000..f83920847e3 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/ExtensionUtilsTest.java @@ -0,0 +1,88 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.bootstrap; + +import org.junit.Test; +import org.sonar.api.BatchComponent; +import org.sonar.api.batch.BatchSide; +import org.sonar.api.batch.InstantiationStrategy; +import org.sonar.api.server.ServerSide; + +import static org.assertj.core.api.Assertions.assertThat; + +public class ExtensionUtilsTest { + + @Test + public void shouldBeBatchInstantiationStrategy() { + assertThat(ExtensionUtils.isInstantiationStrategy(BatchService.class, InstantiationStrategy.PER_BATCH)).isTrue(); + assertThat(ExtensionUtils.isInstantiationStrategy(new BatchService(), InstantiationStrategy.PER_BATCH)).isTrue(); + assertThat(ExtensionUtils.isInstantiationStrategy(ProjectService.class, InstantiationStrategy.PER_BATCH)).isFalse(); + assertThat(ExtensionUtils.isInstantiationStrategy(new ProjectService(), InstantiationStrategy.PER_BATCH)).isFalse(); + assertThat(ExtensionUtils.isInstantiationStrategy(DefaultService.class, InstantiationStrategy.PER_BATCH)).isFalse(); + assertThat(ExtensionUtils.isInstantiationStrategy(new DefaultService(), InstantiationStrategy.PER_BATCH)).isFalse(); + } + + @Test + public void shouldBeProjectInstantiationStrategy() { + assertThat(ExtensionUtils.isInstantiationStrategy(BatchService.class, InstantiationStrategy.PER_PROJECT)).isFalse(); + assertThat(ExtensionUtils.isInstantiationStrategy(new BatchService(), InstantiationStrategy.PER_PROJECT)).isFalse(); + assertThat(ExtensionUtils.isInstantiationStrategy(ProjectService.class, InstantiationStrategy.PER_PROJECT)).isTrue(); + assertThat(ExtensionUtils.isInstantiationStrategy(new ProjectService(), InstantiationStrategy.PER_PROJECT)).isTrue(); + assertThat(ExtensionUtils.isInstantiationStrategy(DefaultService.class, InstantiationStrategy.PER_PROJECT)).isTrue(); + assertThat(ExtensionUtils.isInstantiationStrategy(new DefaultService(), InstantiationStrategy.PER_PROJECT)).isTrue(); + } + + @Test + public void testIsBatchSide() { + assertThat(ExtensionUtils.isBatchSide(BatchService.class)).isTrue(); + assertThat(ExtensionUtils.isBatchSide(new BatchService())).isTrue(); + assertThat(ExtensionUtils.isBatchSide(DeprecatedBatchService.class)).isTrue(); + + assertThat(ExtensionUtils.isBatchSide(ServerService.class)).isFalse(); + assertThat(ExtensionUtils.isBatchSide(new ServerService())).isFalse(); + } + + @BatchSide + @InstantiationStrategy(InstantiationStrategy.PER_BATCH) + public static class BatchService { + + } + + public static class DeprecatedBatchService implements BatchComponent { + + } + + @BatchSide + @InstantiationStrategy(InstantiationStrategy.PER_PROJECT) + public static class ProjectService { + + } + + @BatchSide + public static class DefaultService { + + } + + @ServerSide + public static class ServerService { + + } + +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/FileCacheProviderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/FileCacheProviderTest.java new file mode 100644 index 00000000000..09ce7835e0c --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/FileCacheProviderTest.java @@ -0,0 +1,65 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.bootstrap; + +import java.io.File; +import java.io.IOException; + +import org.junit.Rule; +import org.junit.rules.TemporaryFolder; +import org.junit.Test; +import org.sonar.api.config.Settings; +import org.sonar.home.cache.FileCache; +import static org.assertj.core.api.Assertions.assertThat; + +public class FileCacheProviderTest { + @Rule + public TemporaryFolder temp = new TemporaryFolder(); + + @Test + public void provide() { + FileCacheProvider provider = new FileCacheProvider(); + FileCache cache = provider.provide(new Settings()); + + assertThat(cache).isNotNull(); + assertThat(cache.getDir()).isNotNull().exists(); + } + + @Test + public void keep_singleton_instance() { + FileCacheProvider provider = new FileCacheProvider(); + Settings settings = new Settings(); + FileCache cache1 = provider.provide(settings); + FileCache cache2 = provider.provide(settings); + + assertThat(cache1).isSameAs(cache2); + } + + @Test + public void honor_sonarUserHome() throws IOException { + FileCacheProvider provider = new FileCacheProvider(); + Settings settings = new Settings(); + File f = temp.newFolder(); + settings.appendProperty("sonar.userHome", f.getAbsolutePath()); + FileCache cache = provider.provide(settings); + + assertThat(cache.getDir()).isEqualTo(new File(f, "cache")); + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/GlobalContainerTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/GlobalContainerTest.java new file mode 100644 index 00000000000..41152b3ccb5 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/GlobalContainerTest.java @@ -0,0 +1,76 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.bootstrap; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Lists; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.sonar.api.CoreProperties; +import org.sonar.api.batch.BatchSide; +import org.sonar.api.utils.TempFolder; +import org.sonar.core.util.UuidFactory; + +import static org.assertj.core.api.Assertions.assertThat; + +public class GlobalContainerTest { + @Rule + public TemporaryFolder temp = new TemporaryFolder(); + + private GlobalContainer createContainer(List<Object> extensions) { + Map<String, String> props = ImmutableMap.of(CoreProperties.WORKING_DIRECTORY, temp.getRoot().getAbsolutePath(), + CoreProperties.GLOBAL_WORKING_DIRECTORY, temp.getRoot().getAbsolutePath()); + + GlobalContainer container = GlobalContainer.create(props, extensions, false); + container.doBeforeStart(); + return container; + } + + @Test + public void should_add_components() { + GlobalContainer container = createContainer(Collections.emptyList()); + + assertThat(container.getComponentByType(UuidFactory.class)).isNotNull(); + assertThat(container.getComponentByType(TempFolder.class)).isNotNull(); + } + + @Test + public void should_add_bootstrap_extensions() { + GlobalContainer container = createContainer(Lists.newArrayList(Foo.class, new Bar())); + + assertThat(container.getComponentByType(Foo.class)).isNotNull(); + assertThat(container.getComponentByType(Bar.class)).isNotNull(); + } + + @BatchSide + public static class Foo { + + } + + @BatchSide + public static class Bar { + + } + +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/GlobalModeTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/GlobalModeTest.java new file mode 100644 index 00000000000..5da1858e33b --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/GlobalModeTest.java @@ -0,0 +1,89 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.bootstrap; + +import org.junit.Rule; +import org.junit.rules.ExpectedException; +import org.sonar.api.CoreProperties; +import org.junit.Test; + +import java.util.HashMap; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; + +public class GlobalModeTest { + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Test + public void testModeNotSupported() { + thrown.expect(IllegalStateException.class); + thrown.expectMessage("[preview, publish, issues]"); + + createMode(CoreProperties.ANALYSIS_MODE, "invalid"); + } + + @Test + public void testOtherProperty() { + GlobalMode mode = createMode(CoreProperties.ANALYSIS_MODE, CoreProperties.ANALYSIS_MODE_PUBLISH); + assertThat(mode.isPreview()).isFalse(); + assertThat(mode.isIssues()).isFalse(); + assertThat(mode.isPublish()).isTrue(); + } + + @Test + public void testIssuesMode() { + GlobalMode mode = createMode(CoreProperties.ANALYSIS_MODE, CoreProperties.ANALYSIS_MODE_ISSUES); + assertThat(mode.isPreview()).isFalse(); + assertThat(mode.isIssues()).isTrue(); + assertThat(mode.isPublish()).isFalse(); + } + + @Test + public void preview_mode_fallback_issues() { + GlobalMode mode = createMode(CoreProperties.ANALYSIS_MODE, CoreProperties.ANALYSIS_MODE_PREVIEW); + + assertThat(mode.isIssues()).isTrue(); + assertThat(mode.isPreview()).isFalse(); + } + + @Test + public void testDefault() { + GlobalMode mode = createMode(null, null); + assertThat(mode.isPreview()).isFalse(); + assertThat(mode.isIssues()).isFalse(); + assertThat(mode.isPublish()).isTrue(); + } + + @Test(expected = IllegalStateException.class) + public void testInvalidMode() { + createMode(CoreProperties.ANALYSIS_MODE, CoreProperties.ANALYSIS_MODE_ANALYSIS); + } + + private GlobalMode createMode(String key, String value) { + Map<String, String> map = new HashMap<>(); + if (key != null) { + map.put(key, value); + } + GlobalProperties props = new GlobalProperties(map); + return new GlobalMode(props); + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/GlobalPropertiesTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/GlobalPropertiesTest.java new file mode 100644 index 00000000000..4f3fd5cebca --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/GlobalPropertiesTest.java @@ -0,0 +1,43 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.bootstrap; + +import com.google.common.collect.Maps; +import org.junit.Test; + +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.entry; + +public class GlobalPropertiesTest { + @Test + public void test_copy_of_properties() { + Map<String, String> map = Maps.newHashMap(); + map.put("foo", "bar"); + + GlobalProperties wrapper = new GlobalProperties(map); + assertThat(wrapper.properties()).containsOnly(entry("foo", "bar")); + assertThat(wrapper.properties()).isNotSameAs(map); + + map.put("put", "after_copy"); + assertThat(wrapper.properties()).hasSize(1); + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/GlobalSettingsTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/GlobalSettingsTest.java new file mode 100644 index 00000000000..aa13b14ccb5 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/GlobalSettingsTest.java @@ -0,0 +1,79 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.bootstrap; + +import java.util.Collections; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.sonar.api.config.PropertyDefinitions; +import org.sonar.api.utils.log.LogTester; +import org.sonar.api.utils.log.LoggerLevel; +import org.sonar.scanner.protocol.input.GlobalRepositories; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + +public class GlobalSettingsTest { + + public static final String SOME_VALUE = "some_value"; + @Rule + public ExpectedException thrown = ExpectedException.none(); + @Rule + public LogTester logTester = new LogTester(); + + GlobalRepositories globalRef; + GlobalProperties bootstrapProps; + + private GlobalMode mode; + + @Before + public void prepare() { + globalRef = new GlobalRepositories(); + bootstrapProps = new GlobalProperties(Collections.<String, String>emptyMap()); + mode = mock(GlobalMode.class); + } + + @Test + public void should_load_global_settings() { + globalRef.globalSettings().put("sonar.cpd.cross", "true"); + + GlobalSettings batchSettings = new GlobalSettings(bootstrapProps, new PropertyDefinitions(), globalRef, mode); + + assertThat(batchSettings.getBoolean("sonar.cpd.cross")).isTrue(); + } + + @Test + public void should_log_warn_msg_for_each_jdbc_property_if_present() { + globalRef.globalSettings().put("sonar.jdbc.url", SOME_VALUE); + globalRef.globalSettings().put("sonar.jdbc.username", SOME_VALUE); + globalRef.globalSettings().put("sonar.jdbc.password", SOME_VALUE); + + new GlobalSettings(bootstrapProps, new PropertyDefinitions(), globalRef, mode); + + assertThat(logTester.logs(LoggerLevel.WARN)).containsOnly( + "Property 'sonar.jdbc.url' is not supported any more. It will be ignored. There is no longer any DB connection to the SQ database.", + "Property 'sonar.jdbc.username' is not supported any more. It will be ignored. There is no longer any DB connection to the SQ database.", + "Property 'sonar.jdbc.password' is not supported any more. It will be ignored. There is no longer any DB connection to the SQ database." + ); + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/GlobalTempFolderProviderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/GlobalTempFolderProviderTest.java new file mode 100644 index 00000000000..e0d4ee9d3ea --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/GlobalTempFolderProviderTest.java @@ -0,0 +1,142 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.bootstrap; + +import org.sonar.api.utils.System2; +import org.apache.commons.io.FileUtils; +import org.sonar.api.utils.TempFolder; +import com.google.common.collect.ImmutableMap; +import org.sonar.api.CoreProperties; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.attribute.BasicFileAttributeView; +import java.nio.file.attribute.FileTime; +import java.util.Collections; +import java.util.concurrent.TimeUnit; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.assertj.core.api.Assertions.assertThat; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +public class GlobalTempFolderProviderTest { + @Rule + public TemporaryFolder temp = new TemporaryFolder(); + + private GlobalTempFolderProvider tempFolderProvider = new GlobalTempFolderProvider(); + + @Test + public void createTempFolderProps() throws Exception { + File workingDir = temp.newFolder(); + + TempFolder tempFolder = tempFolderProvider.provide(new GlobalProperties(ImmutableMap.of(CoreProperties.GLOBAL_WORKING_DIRECTORY, workingDir.getAbsolutePath()))); + tempFolder.newDir(); + tempFolder.newFile(); + assertThat(getCreatedTempDir(workingDir)).exists(); + assertThat(getCreatedTempDir(workingDir).list()).hasSize(2); + + FileUtils.deleteQuietly(workingDir); + } + + @Test + public void cleanUpOld() throws IOException { + long creationTime = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(100); + File workingDir = temp.newFolder(); + + for (int i = 0; i < 3; i++) { + File tmp = new File(workingDir, ".sonartmp_" + i); + tmp.mkdirs(); + setFileCreationDate(tmp, creationTime); + } + + tempFolderProvider.provide(new GlobalProperties(ImmutableMap.of(CoreProperties.GLOBAL_WORKING_DIRECTORY, workingDir.getAbsolutePath()))); + // this also checks that all other temps were deleted + assertThat(getCreatedTempDir(workingDir)).exists(); + + FileUtils.deleteQuietly(workingDir); + } + + @Test + public void createTempFolderSonarHome() throws Exception { + // with sonar home, it will be in {sonar.home}/.sonartmp + File sonarHome = temp.newFolder(); + File workingDir = new File(sonarHome, CoreProperties.GLOBAL_WORKING_DIRECTORY_DEFAULT_VALUE).getAbsoluteFile(); + + TempFolder tempFolder = tempFolderProvider.provide(new GlobalProperties(ImmutableMap.of("sonar.userHome", sonarHome.getAbsolutePath()))); + tempFolder.newDir(); + tempFolder.newFile(); + assertThat(getCreatedTempDir(workingDir)).exists(); + assertThat(getCreatedTempDir(workingDir).list()).hasSize(2); + + FileUtils.deleteQuietly(sonarHome); + } + + @Test + public void createTempFolderDefault() throws Exception { + System2 system = mock(System2.class); + tempFolderProvider = new GlobalTempFolderProvider(system); + File userHome = temp.newFolder(); + + when(system.envVariable("SONAR_USER_HOME")).thenReturn(null); + when(system.property("user.home")).thenReturn(userHome.getAbsolutePath().toString()); + + // if nothing is defined, it will be in {user.home}/.sonar/.sonartmp + File defaultSonarHome = new File(userHome.getAbsolutePath(), ".sonar"); + File workingDir = new File(defaultSonarHome, CoreProperties.GLOBAL_WORKING_DIRECTORY_DEFAULT_VALUE).getAbsoluteFile(); + try { + TempFolder tempFolder = tempFolderProvider.provide(new GlobalProperties(Collections.<String, String>emptyMap())); + tempFolder.newDir(); + tempFolder.newFile(); + assertThat(getCreatedTempDir(workingDir)).exists(); + assertThat(getCreatedTempDir(workingDir).list()).hasSize(2); + } finally { + FileUtils.deleteQuietly(workingDir); + } + } + + @Test + public void dotWorkingDir() throws IOException { + File sonarHome = temp.getRoot(); + String globalWorkDir = "."; + GlobalProperties globalProperties = new GlobalProperties(ImmutableMap.of("sonar.userHome", sonarHome.getAbsolutePath(), + CoreProperties.GLOBAL_WORKING_DIRECTORY, globalWorkDir)); + + TempFolder tempFolder = tempFolderProvider.provide(globalProperties); + File newFile = tempFolder.newFile(); + assertThat(newFile.getParentFile().getParentFile().getAbsolutePath()).isEqualTo(sonarHome.getAbsolutePath()); + assertThat(newFile.getParentFile().getName()).startsWith(".sonartmp_"); + } + + private File getCreatedTempDir(File workingDir) { + assertThat(workingDir).isDirectory(); + assertThat(workingDir.listFiles()).hasSize(1); + return workingDir.listFiles()[0]; + } + + private void setFileCreationDate(File f, long time) throws IOException { + BasicFileAttributeView attributes = Files.getFileAttributeView(f.toPath(), BasicFileAttributeView.class); + FileTime creationTime = FileTime.fromMillis(time); + attributes.setTimes(creationTime, creationTime, creationTime); + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/MetricProviderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/MetricProviderTest.java new file mode 100644 index 00000000000..8d1ab306b8d --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/MetricProviderTest.java @@ -0,0 +1,55 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.bootstrap; + +import org.junit.Test; +import org.sonar.api.measures.CoreMetrics; +import org.sonar.api.measures.Metric; +import org.sonar.api.measures.Metrics; + +import java.util.Arrays; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +public class MetricProviderTest { + @Test + public void should_provide_at_least_core_metrics() { + MetricProvider provider = new MetricProvider(); + List<Metric> metrics = provider.provide(); + + assertThat(metrics).hasSize(CoreMetrics.getMetrics().size()); + assertThat(metrics).extracting("key").contains("ncloc"); + } + + @Test + public void should_provide_plugin_metrics() { + Metrics factory = new Metrics() { + public List<Metric> getMetrics() { + return Arrays.<Metric>asList(new Metric.Builder("custom", "Custom", Metric.ValueType.FLOAT).create()); + } + }; + MetricProvider provider = new MetricProvider(new Metrics[] {factory}); + List<Metric> metrics = provider.provide(); + + assertThat(metrics.size()).isEqualTo(1 + CoreMetrics.getMetrics().size()); + assertThat(metrics).extracting("key").contains("custom"); + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/MockHttpServer.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/MockHttpServer.java new file mode 100644 index 00000000000..99f10de2c9e --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/MockHttpServer.java @@ -0,0 +1,118 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.bootstrap; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.apache.commons.io.IOUtils; +import org.eclipse.jetty.server.Handler; +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.handler.AbstractHandler; + +import static javax.servlet.http.HttpServletResponse.SC_OK; +import static org.apache.commons.io.IOUtils.write; + +public class MockHttpServer { + private Server server; + private String responseBody; + private String requestBody; + private String mockResponseData; + private int mockResponseStatus = SC_OK; + private List<String> targets = new ArrayList<>(); + + public void start() throws Exception { + server = new Server(0); + server.setHandler(getMockHandler()); + server.start(); + } + + public int getNumberRequests() { + return targets.size(); + } + + /** + * Creates an {@link org.mortbay.jetty.handler.AbstractHandler handler} returning an arbitrary String as a response. + * + * @return never <code>null</code>. + */ + public Handler getMockHandler() { + Handler handler = new AbstractHandler() { + + public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { + targets.add(target); + setResponseBody(getMockResponseData()); + setRequestBody(IOUtils.toString(baseRequest.getInputStream())); + response.setStatus(mockResponseStatus); + response.setContentType("text/xml;charset=utf-8"); + write(getResponseBody(), response.getOutputStream()); + baseRequest.setHandled(true); + } + }; + return handler; + } + + public void stop() { + try { + if (server != null) { + server.stop(); + } + } catch (Exception e) { + throw new IllegalStateException("Fail to stop HTTP server", e); + } + } + + public String getResponseBody() { + return responseBody; + } + + public void setResponseBody(String responseBody) { + this.responseBody = responseBody; + } + + public String getRequestBody() { + return requestBody; + } + + public void setRequestBody(String requestBody) { + this.requestBody = requestBody; + } + + public void setMockResponseStatus(int status) { + this.mockResponseStatus = status; + } + + public String getMockResponseData() { + return mockResponseData; + } + + public void setMockResponseData(String mockResponseData) { + this.mockResponseData = mockResponseData; + } + + public int getPort() { + return server.getConnectors()[0].getLocalPort(); + } + +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrapper/BatchTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrapper/BatchTest.java new file mode 100644 index 00000000000..b9696631f89 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrapper/BatchTest.java @@ -0,0 +1,73 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.bootstrapper; + +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.mockito.Mockito.mock; + +public class BatchTest { + @Test + public void testBuilder() { + Batch batch = newBatch(); + assertNotNull(batch); + + } + + private Batch newBatch() { + return Batch.builder() + .setEnvironment(new EnvironmentInformation("Gradle", "1.0")) + .addComponent("fake") + .build(); + } + + @Test(expected = IllegalStateException.class) + public void shouldFailIfNullComponents() { + Batch.builder() + .setEnvironment(new EnvironmentInformation("Gradle", "1.0")) + .setComponents(null) + .build(); + } + + @Test + public void shouldDisableLoggingConfiguration() { + Batch batch = Batch.builder() + .setEnvironment(new EnvironmentInformation("Gradle", "1.0")) + .addComponent("fake") + .setEnableLoggingConfiguration(false) + .build(); + assertNull(batch.getLoggingConfiguration()); + } + + @Test + public void loggingConfigurationShouldBeEnabledByDefault() { + assertNotNull(newBatch().getLoggingConfiguration()); + } + + @Test + public void shoudSetLogListener() { + LogOutput logOutput = mock(LogOutput.class); + Batch batch = Batch.builder().setLogOutput(logOutput).build(); + assertThat(batch.getLoggingConfiguration().getLogOutput()).isEqualTo(logOutput); + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrapper/EnvironmentInformationTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrapper/EnvironmentInformationTest.java new file mode 100644 index 00000000000..71dd0495d49 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrapper/EnvironmentInformationTest.java @@ -0,0 +1,41 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.bootstrapper; + +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class EnvironmentInformationTest { + @Test + public void test_bean() { + EnvironmentInformation env = new EnvironmentInformation("Maven Plugin", "2.0"); + + assertThat(env.getKey()).isEqualTo("Maven Plugin"); + assertThat(env.getVersion()).isEqualTo("2.0"); + } + + @Test + public void test_toString() { + EnvironmentInformation env = new EnvironmentInformation("Maven Plugin", "2.0"); + + assertThat(env.toString()).isEqualTo("Maven Plugin/2.0"); + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrapper/LogCallbackAppenderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrapper/LogCallbackAppenderTest.java new file mode 100644 index 00000000000..6b225306fe0 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrapper/LogCallbackAppenderTest.java @@ -0,0 +1,76 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.bootstrapper; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.spi.ILoggingEvent; +import org.junit.Before; +import org.junit.Test; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +public class LogCallbackAppenderTest { + private LogOutput listener; + private LogCallbackAppender appender; + private ILoggingEvent event; + + @Before + public void setUp() { + listener = mock(LogOutput.class); + appender = new LogCallbackAppender(listener); + } + + @Test + public void testLevelTranslation() { + testMessage("test", Level.INFO, LogOutput.Level.INFO); + testMessage("test", Level.DEBUG, LogOutput.Level.DEBUG); + testMessage("test", Level.ERROR, LogOutput.Level.ERROR); + testMessage("test", Level.TRACE, LogOutput.Level.TRACE); + testMessage("test", Level.WARN, LogOutput.Level.WARN); + + // this should never happen + testMessage("test", Level.OFF, LogOutput.Level.DEBUG); + } + + private void testMessage(String msg, Level level, LogOutput.Level translatedLevel) { + reset(listener); + event = mock(ILoggingEvent.class); + when(event.getFormattedMessage()).thenReturn(msg); + when(event.getLevel()).thenReturn(level); + + appender.append(event); + + verify(event).getFormattedMessage(); + verify(event).getLevel(); + verify(listener).log(msg, translatedLevel); + verifyNoMoreInteractions(event, listener); + } + + @Test + public void testChangeTarget() { + listener = mock(LogOutput.class); + appender.setTarget(listener); + testLevelTranslation(); + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrapper/LoggingConfigurationTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrapper/LoggingConfigurationTest.java new file mode 100644 index 00000000000..920af566e93 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrapper/LoggingConfigurationTest.java @@ -0,0 +1,167 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.bootstrapper; + +import com.google.common.collect.Maps; +import java.util.Map; +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + +public class LoggingConfigurationTest { + + @Test + public void testSetVerbose() { + assertThat(new LoggingConfiguration(null).setVerbose(true) + .getSubstitutionVariable(LoggingConfiguration.PROPERTY_ROOT_LOGGER_LEVEL)).isEqualTo(LoggingConfiguration.LEVEL_ROOT_VERBOSE); + + assertThat(new LoggingConfiguration(null).setVerbose(false) + .getSubstitutionVariable(LoggingConfiguration.PROPERTY_ROOT_LOGGER_LEVEL)).isEqualTo(LoggingConfiguration.LEVEL_ROOT_DEFAULT); + + assertThat(new LoggingConfiguration(null).setRootLevel("ERROR") + .getSubstitutionVariable(LoggingConfiguration.PROPERTY_ROOT_LOGGER_LEVEL)).isEqualTo("ERROR"); + } + + @Test + public void testSetVerboseAnalysis() { + Map<String, String> globalProps = Maps.newHashMap(); + LoggingConfiguration conf = new LoggingConfiguration(null).setProperties(globalProps); + assertThat(conf.getSubstitutionVariable(LoggingConfiguration.PROPERTY_ROOT_LOGGER_LEVEL)).isEqualTo(LoggingConfiguration.LEVEL_ROOT_DEFAULT); + assertThat(conf.getSubstitutionVariable(LoggingConfiguration.PROPERTY_SQL_LOGGER_LEVEL)).isEqualTo("WARN"); + + Map<String, String> analysisProperties = Maps.newHashMap(); + analysisProperties.put("sonar.verbose", "true"); + + conf.setProperties(analysisProperties, globalProps); + assertThat(conf.getSubstitutionVariable(LoggingConfiguration.PROPERTY_ROOT_LOGGER_LEVEL)).isEqualTo(LoggingConfiguration.LEVEL_ROOT_VERBOSE); + assertThat(conf.getSubstitutionVariable(LoggingConfiguration.PROPERTY_SQL_LOGGER_LEVEL)).isEqualTo("WARN"); + } + + @Test + public void testOverrideVerbose() { + Map<String, String> globalProps = Maps.newHashMap(); + globalProps.put("sonar.verbose", "true"); + LoggingConfiguration conf = new LoggingConfiguration(null).setProperties(globalProps); + assertThat(conf.getSubstitutionVariable(LoggingConfiguration.PROPERTY_ROOT_LOGGER_LEVEL)).isEqualTo(LoggingConfiguration.LEVEL_ROOT_VERBOSE); + assertThat(conf.getSubstitutionVariable(LoggingConfiguration.PROPERTY_SQL_LOGGER_LEVEL)).isEqualTo("WARN"); + + Map<String, String> analysisProperties = Maps.newHashMap(); + analysisProperties.put("sonar.verbose", "false"); + + conf.setProperties(analysisProperties, globalProps); + assertThat(conf.getSubstitutionVariable(LoggingConfiguration.PROPERTY_ROOT_LOGGER_LEVEL)).isEqualTo(LoggingConfiguration.LEVEL_ROOT_DEFAULT); + assertThat(conf.getSubstitutionVariable(LoggingConfiguration.PROPERTY_SQL_LOGGER_LEVEL)).isEqualTo("WARN"); + } + + @Test + public void shouldNotBeVerboseByDefault() { + assertThat(new LoggingConfiguration(null) + .getSubstitutionVariable(LoggingConfiguration.PROPERTY_ROOT_LOGGER_LEVEL)).isEqualTo(LoggingConfiguration.LEVEL_ROOT_DEFAULT); + } + + @Test + public void test_log_listener_setter() { + LogOutput listener = mock(LogOutput.class); + assertThat(new LoggingConfiguration(null).setLogOutput(listener).getLogOutput()).isEqualTo(listener); + } + + @Test + public void test_deprecated_log_properties() { + Map<String, String> properties = Maps.newHashMap(); + assertThat(new LoggingConfiguration(null).setProperties(properties) + .getSubstitutionVariable(LoggingConfiguration.PROPERTY_ROOT_LOGGER_LEVEL)).isEqualTo(LoggingConfiguration.LEVEL_ROOT_DEFAULT); + + properties.put("sonar.verbose", "true"); + LoggingConfiguration conf = new LoggingConfiguration(null).setProperties(properties); + assertThat(conf.getSubstitutionVariable(LoggingConfiguration.PROPERTY_ROOT_LOGGER_LEVEL)).isEqualTo(LoggingConfiguration.LEVEL_ROOT_VERBOSE); + assertThat(conf.getSubstitutionVariable(LoggingConfiguration.PROPERTY_SQL_LOGGER_LEVEL)).isEqualTo("WARN"); + + properties.put("sonar.verbose", "false"); + conf = new LoggingConfiguration(null).setProperties(properties); + assertThat(conf.getSubstitutionVariable(LoggingConfiguration.PROPERTY_ROOT_LOGGER_LEVEL)).isEqualTo(LoggingConfiguration.LEVEL_ROOT_DEFAULT); + assertThat(conf.getSubstitutionVariable(LoggingConfiguration.PROPERTY_SQL_LOGGER_LEVEL)).isEqualTo("WARN"); + + properties.put("sonar.verbose", "false"); + properties.put("sonar.log.profilingLevel", "FULL"); + conf = new LoggingConfiguration(null).setProperties(properties); + assertThat(conf.getSubstitutionVariable(LoggingConfiguration.PROPERTY_ROOT_LOGGER_LEVEL)).isEqualTo("DEBUG"); + assertThat(conf.getSubstitutionVariable(LoggingConfiguration.PROPERTY_SQL_LOGGER_LEVEL)).isEqualTo("TRACE"); + + properties.put("sonar.verbose", "false"); + properties.put("sonar.log.profilingLevel", "BASIC"); + conf = new LoggingConfiguration(null).setProperties(properties); + assertThat(conf.getSubstitutionVariable(LoggingConfiguration.PROPERTY_ROOT_LOGGER_LEVEL)).isEqualTo("DEBUG"); + assertThat(conf.getSubstitutionVariable(LoggingConfiguration.PROPERTY_SQL_LOGGER_LEVEL)).isEqualTo("WARN"); + } + + @Test + public void test_log_level_property() { + Map<String, String> properties = Maps.newHashMap(); + LoggingConfiguration conf = new LoggingConfiguration(null).setProperties(properties); + assertThat(conf.getSubstitutionVariable(LoggingConfiguration.PROPERTY_ROOT_LOGGER_LEVEL)).isEqualTo("INFO"); + assertThat(conf.getSubstitutionVariable(LoggingConfiguration.PROPERTY_SQL_LOGGER_LEVEL)).isEqualTo("WARN"); + + properties.put("sonar.log.level", "INFO"); + conf = new LoggingConfiguration(null).setProperties(properties); + assertThat(conf.getSubstitutionVariable(LoggingConfiguration.PROPERTY_ROOT_LOGGER_LEVEL)).isEqualTo("INFO"); + assertThat(conf.getSubstitutionVariable(LoggingConfiguration.PROPERTY_SQL_LOGGER_LEVEL)).isEqualTo("WARN"); + + properties.put("sonar.log.level", "DEBUG"); + conf = new LoggingConfiguration(null).setProperties(properties); + assertThat(conf.getSubstitutionVariable(LoggingConfiguration.PROPERTY_ROOT_LOGGER_LEVEL)).isEqualTo("DEBUG"); + assertThat(conf.getSubstitutionVariable(LoggingConfiguration.PROPERTY_SQL_LOGGER_LEVEL)).isEqualTo("WARN"); + + properties.put("sonar.log.level", "TRACE"); + conf = new LoggingConfiguration(null).setProperties(properties); + assertThat(conf.getSubstitutionVariable(LoggingConfiguration.PROPERTY_ROOT_LOGGER_LEVEL)).isEqualTo("DEBUG"); + assertThat(conf.getSubstitutionVariable(LoggingConfiguration.PROPERTY_SQL_LOGGER_LEVEL)).isEqualTo("TRACE"); + } + + @Test + public void testDefaultFormat() { + assertThat(new LoggingConfiguration(null) + .getSubstitutionVariable(LoggingConfiguration.PROPERTY_FORMAT)).isEqualTo(LoggingConfiguration.FORMAT_DEFAULT); + } + + @Test + public void testMavenFormat() { + assertThat(new LoggingConfiguration(new EnvironmentInformation("maven", "1.0")) + .getSubstitutionVariable(LoggingConfiguration.PROPERTY_FORMAT)).isEqualTo(LoggingConfiguration.FORMAT_MAVEN); + } + + @Test + public void testSetFormat() { + assertThat(new LoggingConfiguration(null).setFormat("%d %level") + .getSubstitutionVariable(LoggingConfiguration.PROPERTY_FORMAT)).isEqualTo("%d %level"); + } + + @Test + public void shouldNotSetBlankFormat() { + assertThat(new LoggingConfiguration(null).setFormat(null) + .getSubstitutionVariable(LoggingConfiguration.PROPERTY_FORMAT)).isEqualTo(LoggingConfiguration.FORMAT_DEFAULT); + + assertThat(new LoggingConfiguration(null).setFormat("") + .getSubstitutionVariable(LoggingConfiguration.PROPERTY_FORMAT)).isEqualTo(LoggingConfiguration.FORMAT_DEFAULT); + + assertThat(new LoggingConfiguration(null).setFormat(" ") + .getSubstitutionVariable(LoggingConfiguration.PROPERTY_FORMAT)).isEqualTo(LoggingConfiguration.FORMAT_DEFAULT); + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrapper/LoggingConfiguratorTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrapper/LoggingConfiguratorTest.java new file mode 100644 index 00000000000..2d719e4a657 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrapper/LoggingConfiguratorTest.java @@ -0,0 +1,184 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.bootstrapper; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PrintStream; +import java.io.UnsupportedEncodingException; +import java.nio.charset.StandardCharsets; +import org.apache.commons.io.IOUtils; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static org.assertj.core.api.Assertions.assertThat; + +public class LoggingConfiguratorTest { + private static final String DEFAULT_CLASSPATH_CONF = "/org/sonar/batch/bootstrapper/logback.xml"; + private static final String TEST_STR = "foo"; + private LoggingConfiguration conf = new LoggingConfiguration(); + private ByteArrayOutputStream out; + private SimpleLogListener listener; + @Rule + public TemporaryFolder folder = new TemporaryFolder(); + + @Before + public void setUp() { + out = new ByteArrayOutputStream(); + conf = new LoggingConfiguration(); + listener = new SimpleLogListener(); + } + + private class SimpleLogListener implements LogOutput { + String msg; + LogOutput.Level level; + + @Override + public void log(String msg, LogOutput.Level level) { + this.msg = msg; + this.level = level; + } + } + + @Test + public void testWithFile() throws FileNotFoundException, IOException { + InputStream is = this.getClass().getResourceAsStream(DEFAULT_CLASSPATH_CONF); + File tmpFolder = folder.getRoot(); + File testFile = new File(tmpFolder, "test"); + OutputStream os = new FileOutputStream(testFile); + IOUtils.copy(is, os); + os.close(); + + conf.setLogOutput(listener); + LoggingConfigurator.apply(conf, testFile); + + Logger logger = LoggerFactory.getLogger(this.getClass()); + logger.info(TEST_STR); + + assertThat(listener.msg).endsWith(TEST_STR); + assertThat(listener.level).isEqualTo(LogOutput.Level.INFO); + } + + @Test + public void testCustomAppender() throws UnsupportedEncodingException { + conf.setLogOutput(listener); + LoggingConfigurator.apply(conf); + + Logger logger = LoggerFactory.getLogger(this.getClass()); + logger.info(TEST_STR); + + assertThat(listener.msg).endsWith(TEST_STR); + assertThat(listener.level).isEqualTo(LogOutput.Level.INFO); + } + + @Test + public void testNoStdout() throws UnsupportedEncodingException { + System.setOut(new PrintStream(out, false, StandardCharsets.UTF_8.name())); + conf.setLogOutput(listener); + LoggingConfigurator.apply(conf); + + Logger logger = LoggerFactory.getLogger(this.getClass()); + + logger.error(TEST_STR); + logger.info(TEST_STR); + logger.debug(TEST_STR); + assertThat(out.size()).isEqualTo(0); + } + + @Test + public void testConfigureMultipleTimes() throws UnsupportedEncodingException { + System.setOut(new PrintStream(out, false, StandardCharsets.UTF_8.name())); + conf.setLogOutput(listener); + LoggingConfigurator.apply(conf); + + Logger logger = LoggerFactory.getLogger(this.getClass()); + logger.debug("debug"); + assertThat(listener.msg).isNull(); + + conf.setVerbose(true); + LoggingConfigurator.apply(conf); + + logger.debug("debug"); + assertThat(listener.msg).isEqualTo("debug"); + } + + @Test + public void testFormatNoEffect() throws UnsupportedEncodingException { + conf.setLogOutput(listener); + conf.setFormat("%t"); + + LoggingConfigurator.apply(conf); + Logger logger = LoggerFactory.getLogger(this.getClass()); + + logger.info("info"); + + assertThat(listener.msg).isEqualTo("info"); + } + + @Test + public void testSqlClasspath() throws UnsupportedEncodingException { + String classpath = "/org/sonar/batch/bootstrapper/logback.xml"; + + conf.setLogOutput(listener); + conf.setShowSql(true); + + LoggingConfigurator.apply(conf, classpath); + + Logger logger = LoggerFactory.getLogger("java.sql"); + logger.info("foo"); + + assertThat(listener.msg).endsWith(TEST_STR); + } + + @Test + public void testNoListener() throws UnsupportedEncodingException { + System.setOut(new PrintStream(out, false, StandardCharsets.UTF_8.name())); + LoggingConfigurator.apply(conf); + + Logger logger = LoggerFactory.getLogger(this.getClass()); + logger.info("info"); + + assertThat(new String(out.toByteArray(), StandardCharsets.UTF_8)).contains("info"); + } + + @Test + public void testNoSqlClasspath() throws UnsupportedEncodingException { + String classpath = "/org/sonar/batch/bootstrapper/logback.xml"; + + conf.setLogOutput(listener); + conf.setShowSql(false); + + LoggingConfigurator.apply(conf, classpath); + + Logger logger = LoggerFactory.getLogger("java.sql"); + logger.info("foo"); + + assertThat(listener.msg).isNull(); + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/cache/DefaultProjectCacheStatusTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/cache/DefaultProjectCacheStatusTest.java new file mode 100644 index 00000000000..c8f105de443 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/cache/DefaultProjectCacheStatusTest.java @@ -0,0 +1,91 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.cache; + +import com.google.common.io.Files; +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Date; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.rules.TemporaryFolder; +import org.sonar.home.cache.PersistentCache; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class DefaultProjectCacheStatusTest { + @Rule + public TemporaryFolder tmp = new TemporaryFolder(); + + @Rule + public ExpectedException exception = ExpectedException.none(); + + ProjectCacheStatus cacheStatus; + PersistentCache cache = mock(PersistentCache.class); + + @Before + public void setUp() { + when(cache.getDirectory()).thenReturn(tmp.getRoot().toPath()); + cacheStatus = new DefaultProjectCacheStatus(cache); + } + + @Test + public void errorSave() throws IOException { + when(cache.getDirectory()).thenReturn(tmp.getRoot().toPath().resolve("unexistent_folder")); + cacheStatus = new DefaultProjectCacheStatus(cache); + + exception.expect(IllegalStateException.class); + exception.expectMessage("Failed to write cache sync status"); + cacheStatus.save(); + } + + @Test + public void errorStatus() throws IOException { + Files.write("trash".getBytes(StandardCharsets.UTF_8), new File(tmp.getRoot(), "cache-sync-status")); + cacheStatus = new DefaultProjectCacheStatus(cache); + + exception.expect(IllegalStateException.class); + exception.expectMessage("Failed to read cache sync status"); + cacheStatus.getSyncStatus(); + } + + @Test + public void testSave() { + cacheStatus.save(); + assertThat(cacheStatus.getSyncStatus()).isNotNull(); + assertThat(age(cacheStatus.getSyncStatus())).isLessThan(2000); + } + + @Test + public void testDelete() { + cacheStatus.save(); + cacheStatus.delete(); + assertThat(cacheStatus.getSyncStatus()).isNull(); + } + + private long age(Date date) { + return (new Date().getTime()) - date.getTime(); + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/cache/GlobalPersistentCacheProviderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/cache/GlobalPersistentCacheProviderTest.java new file mode 100644 index 00000000000..3f019caae02 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/cache/GlobalPersistentCacheProviderTest.java @@ -0,0 +1,83 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.cache; + +import org.sonar.home.cache.PersistentCache; + +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.HashMap; + +import static org.junit.Assert.*; +import static org.assertj.core.api.Assertions.assertThat; +import org.sonar.batch.bootstrap.GlobalProperties; +import org.junit.Before; +import org.junit.Test; +import org.junit.Rule; +import org.junit.rules.TemporaryFolder; + +public class GlobalPersistentCacheProviderTest { + @Rule + public TemporaryFolder temp = new TemporaryFolder(); + + private GlobalPersistentCacheProvider provider; + private GlobalProperties globalProperties; + + @Before + public void setUp() { + HashMap<String, String> map = new HashMap<>(); + map.put("sonar.userHome", temp.getRoot().getAbsolutePath()); + globalProperties = new GlobalProperties(map); + provider = new GlobalPersistentCacheProvider(); + } + + @Test + public void test_path() { + PersistentCache cache = provider.provide(globalProperties); + assertThat(cache.getDirectory()).isEqualTo(temp.getRoot().toPath() + .resolve("ws_cache") + .resolve("http%3A%2F%2Flocalhost%3A9000") + .resolve("global")); + } + + @Test + public void test_singleton() { + assertTrue(provider.provide(globalProperties) == provider.provide(globalProperties)); + } + + @Test + public void test_without_sonar_home() { + globalProperties = new GlobalProperties(new HashMap<String, String>()); + PersistentCache cache = provider.provide(globalProperties); + assertThat(cache.getDirectory().toAbsolutePath().toString()).startsWith(findHome().toAbsolutePath().toString()); + + } + + private static Path findHome() { + String home = System.getenv("SONAR_USER_HOME"); + + if (home != null) { + return Paths.get(home); + } + + home = System.getProperty("user.home"); + return Paths.get(home, ".sonar"); + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/cache/NonAssociatedCacheSynchronizerTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/cache/NonAssociatedCacheSynchronizerTest.java new file mode 100644 index 00000000000..c06bb94ca08 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/cache/NonAssociatedCacheSynchronizerTest.java @@ -0,0 +1,93 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.cache; + +import com.google.common.collect.ImmutableList; +import java.util.Date; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.sonar.batch.repository.QualityProfileLoader; +import org.sonar.batch.rule.ActiveRulesLoader; +import org.sonar.batch.rule.LoadedActiveRule; +import org.sonar.batch.rule.RulesLoader; +import org.sonarqube.ws.QualityProfiles.SearchWsResponse.QualityProfile; + +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.when; + +public class NonAssociatedCacheSynchronizerTest { + private NonAssociatedCacheSynchronizer synchronizer; + + @Mock + private RulesLoader rulesLoader; + @Mock + private QualityProfileLoader qualityProfileLoader; + @Mock + private ActiveRulesLoader activeRulesLoader; + @Mock + private ProjectCacheStatus cacheStatus; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + QualityProfile pf = QualityProfile.newBuilder().setKey("profile").setName("profile").setLanguage("lang").build(); + LoadedActiveRule ar = new LoadedActiveRule(); + + when(qualityProfileLoader.loadDefault(null, null)).thenReturn(ImmutableList.of(pf)); + when(activeRulesLoader.load("profile", null)).thenReturn(ImmutableList.of(ar)); + + synchronizer = new NonAssociatedCacheSynchronizer(rulesLoader, qualityProfileLoader, activeRulesLoader, cacheStatus); + } + + @Test + public void dont_sync_if_exists() { + when(cacheStatus.getSyncStatus()).thenReturn(new Date()); + synchronizer.execute(false); + verifyZeroInteractions(rulesLoader, qualityProfileLoader, activeRulesLoader); + } + + @Test + public void always_sync_if_force() { + when(cacheStatus.getSyncStatus()).thenReturn(new Date()); + synchronizer.execute(true); + checkSync(); + } + + @Test + public void sync_if_doesnt_exist() { + synchronizer.execute(false); + checkSync(); + } + + private void checkSync() { + verify(cacheStatus).getSyncStatus(); + verify(cacheStatus).save(); + verify(rulesLoader).load(null); + verify(qualityProfileLoader).loadDefault(null, null); + verify(activeRulesLoader).load("profile", null); + + verifyNoMoreInteractions(qualityProfileLoader, activeRulesLoader); + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/cache/ProjectCacheSynchronizerTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/cache/ProjectCacheSynchronizerTest.java new file mode 100644 index 00000000000..52df42e035d --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/cache/ProjectCacheSynchronizerTest.java @@ -0,0 +1,197 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.cache; + +import com.google.common.base.Function; +import com.google.common.collect.ImmutableList; +import java.io.IOException; +import java.util.Date; +import java.util.HashMap; +import org.apache.commons.lang.mutable.MutableBoolean; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.sonar.api.batch.bootstrap.ProjectDefinition; +import org.sonar.batch.analysis.AnalysisProperties; +import org.sonar.batch.analysis.DefaultAnalysisMode; +import org.sonar.batch.repository.DefaultProjectRepositoriesLoader; +import org.sonar.batch.repository.DefaultQualityProfileLoader; +import org.sonar.batch.repository.DefaultServerIssuesLoader; +import org.sonar.batch.repository.ProjectRepositories; +import org.sonar.batch.repository.ProjectRepositoriesLoader; +import org.sonar.batch.repository.QualityProfileLoader; +import org.sonar.batch.repository.ServerIssuesLoader; +import org.sonar.batch.repository.user.UserRepositoryLoader; +import org.sonar.batch.rule.ActiveRulesLoader; +import org.sonar.batch.rule.DefaultActiveRulesLoader; +import org.sonar.batch.rule.LoadedActiveRule; +import org.sonar.batch.rule.RulesLoader; +import org.sonarqube.ws.QualityProfiles.SearchWsResponse.QualityProfile; + +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyBoolean; +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +public class ProjectCacheSynchronizerTest { + private static final String PROJECT_KEY = "org.codehaus.sonar-plugins:sonar-scm-git-plugin"; + + @Rule + public ExpectedException exception = ExpectedException.none(); + + @Mock + private ProjectDefinition project; + @Mock + private ProjectCacheStatus cacheStatus; + @Mock + private DefaultAnalysisMode analysisMode; + @Mock + private AnalysisProperties properties; + @Mock + private RulesLoader rulesLoader; + + private ServerIssuesLoader issuesLoader; + private UserRepositoryLoader userRepositoryLoader; + private QualityProfileLoader qualityProfileLoader; + private ActiveRulesLoader activeRulesLoader; + private ProjectRepositoriesLoader projectRepositoriesLoader; + + @Before + public void setUp() throws IOException { + MockitoAnnotations.initMocks(this); + + when(analysisMode.isIssues()).thenReturn(true); + when(properties.properties()).thenReturn(new HashMap<String, String>()); + } + + private ProjectCacheSynchronizer createMockedLoaders(boolean projectExists, Date lastAnalysisDate) { + issuesLoader = mock(DefaultServerIssuesLoader.class); + userRepositoryLoader = mock(UserRepositoryLoader.class); + qualityProfileLoader = mock(DefaultQualityProfileLoader.class); + activeRulesLoader = mock(DefaultActiveRulesLoader.class); + projectRepositoriesLoader = mock(DefaultProjectRepositoriesLoader.class); + + QualityProfile pf = QualityProfile.newBuilder().setKey("profile").setName("profile").setLanguage("lang").build(); + LoadedActiveRule ar = new LoadedActiveRule(); + ProjectRepositories repo = mock(ProjectRepositories.class); + + when(qualityProfileLoader.load(PROJECT_KEY, null, null)).thenReturn(ImmutableList.of(pf)); + when(qualityProfileLoader.loadDefault(null, null)).thenReturn(ImmutableList.of(pf)); + when(activeRulesLoader.load("profile", null)).thenReturn(ImmutableList.of(ar)); + when(repo.lastAnalysisDate()).thenReturn(lastAnalysisDate); + when(repo.exists()).thenReturn(projectExists); + when(projectRepositoriesLoader.load(anyString(), anyBoolean(), any(MutableBoolean.class))).thenReturn(repo); + + return new ProjectCacheSynchronizer(rulesLoader, qualityProfileLoader, projectRepositoriesLoader, activeRulesLoader, issuesLoader, userRepositoryLoader, cacheStatus); + } + + @Test + public void testLoadersUsage() { + ProjectCacheSynchronizer synchronizer = createMockedLoaders(true, new Date()); + synchronizer.load(PROJECT_KEY, false); + + verify(issuesLoader).load(eq(PROJECT_KEY), any(Function.class)); + verify(rulesLoader).load(null); + verify(qualityProfileLoader).load(PROJECT_KEY, null, null); + verify(activeRulesLoader).load("profile", null); + verify(projectRepositoriesLoader).load(eq(PROJECT_KEY), eq(true), any(MutableBoolean.class)); + + verifyNoMoreInteractions(issuesLoader, userRepositoryLoader, qualityProfileLoader, activeRulesLoader, projectRepositoriesLoader); + } + + @Test + public void testLoadersUsage_NoLastAnalysis() { + ProjectCacheSynchronizer synchronizer = createMockedLoaders(true, null); + synchronizer.load(PROJECT_KEY, false); + + verify(projectRepositoriesLoader).load(eq(PROJECT_KEY), eq(true), any(MutableBoolean.class)); + verify(qualityProfileLoader).load(PROJECT_KEY, null, null); + verify(activeRulesLoader).load("profile", null); + + verifyNoMoreInteractions(issuesLoader, userRepositoryLoader, qualityProfileLoader, activeRulesLoader, projectRepositoriesLoader); + } + + @Test + public void testLoadersUsage_ProjectDoesntExist() { + ProjectCacheSynchronizer synchronizer = createMockedLoaders(false, null); + synchronizer.load(PROJECT_KEY, false); + + verify(projectRepositoriesLoader).load(eq(PROJECT_KEY), eq(true), any(MutableBoolean.class)); + verify(qualityProfileLoader).loadDefault(null, null); + verify(activeRulesLoader).load("profile", null); + + verifyNoMoreInteractions(issuesLoader, userRepositoryLoader, qualityProfileLoader, activeRulesLoader, projectRepositoriesLoader); + } + + @Test + public void testLastAnalysisToday() { + ProjectCacheSynchronizer synchronizer = createMockedLoaders(true, new Date()); + + when(cacheStatus.getSyncStatus()).thenReturn(new Date()); + synchronizer.load(PROJECT_KEY, false); + + verify(cacheStatus).getSyncStatus(); + verifyNoMoreInteractions(issuesLoader, userRepositoryLoader, qualityProfileLoader, activeRulesLoader, projectRepositoriesLoader, cacheStatus); + } + + @Test + public void testLastAnalysisYesterday() { + ProjectCacheSynchronizer synchronizer = createMockedLoaders(true, new Date()); + + Date d = new Date(new Date().getTime() - 60 * 60 * 24 * 1000); + when(cacheStatus.getSyncStatus()).thenReturn(d); + synchronizer.load(PROJECT_KEY, false); + + verify(cacheStatus).save(); + verify(cacheStatus).getSyncStatus(); + } + + @Test + public void testDontFailOnError() { + ProjectCacheSynchronizer synchronizer = createMockedLoaders(true, new Date()); + + Date d = new Date(new Date().getTime() - 60 * 60 * 24 * 1000); + when(cacheStatus.getSyncStatus()).thenReturn(d); + + when(projectRepositoriesLoader.load(anyString(), anyBoolean(), any(MutableBoolean.class))).thenThrow(IllegalStateException.class); + synchronizer.load(PROJECT_KEY, false); + + verify(cacheStatus).getSyncStatus(); + verifyNoMoreInteractions(cacheStatus); + } + + @Test + public void testForce() { + ProjectCacheSynchronizer synchronizer = createMockedLoaders(true, new Date()); + + when(cacheStatus.getSyncStatus()).thenReturn(new Date()); + synchronizer.load(PROJECT_KEY, true); + + verify(cacheStatus).save(); + verify(cacheStatus).getSyncStatus(); + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/cache/ProjectPersistentCacheProviderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/cache/ProjectPersistentCacheProviderTest.java new file mode 100644 index 00000000000..69c142556ae --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/cache/ProjectPersistentCacheProviderTest.java @@ -0,0 +1,80 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.cache; + +import org.sonar.api.batch.bootstrap.ProjectKey; + +import org.sonar.batch.util.BatchUtils; +import org.sonar.batch.analysis.DefaultAnalysisMode; +import org.junit.Rule; +import org.junit.rules.TemporaryFolder; +import org.sonar.batch.bootstrap.GlobalProperties; +import org.sonar.batch.cache.ProjectPersistentCacheProvider; + +import java.io.File; +import java.nio.file.Path; +import java.util.Collections; + +import static org.mockito.Mockito.mock; +import org.junit.Before; +import static org.assertj.core.api.Assertions.assertThat; +import org.junit.Test; + +public class ProjectPersistentCacheProviderTest { + @Rule + public TemporaryFolder temp = new TemporaryFolder(); + + private ProjectPersistentCacheProvider provider = null; + private GlobalProperties props = null; + private DefaultAnalysisMode mode = null; + private ProjectKey key = null; + + @Before + public void prepare() { + key = new ProjectKeySupplier("proj"); + props = new GlobalProperties(Collections.<String, String>emptyMap()); + mode = mock(DefaultAnalysisMode.class); + provider = new ProjectPersistentCacheProvider(); + } + + @Test + public void test_singleton() { + assertThat(provider.provide(props, mode, key)).isEqualTo(provider.provide(props, mode, key)); + } + + @Test + public void test_cache_dir() { + assertThat(provider.provide(props, mode, key).getDirectory().toFile()).exists().isDirectory(); + } + + @Test + public void test_home() { + File f = temp.getRoot(); + props.properties().put("sonar.userHome", f.getAbsolutePath()); + Path expected = f.toPath() + .resolve("ws_cache") + .resolve("http%3A%2F%2Flocalhost%3A9000") + .resolve( BatchUtils.getServerVersion()) + .resolve("projects") + .resolve("proj"); + + assertThat(provider.provide(props, mode, key).getDirectory()).isEqualTo(expected); + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/cache/ProjectSyncContainerTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/cache/ProjectSyncContainerTest.java new file mode 100644 index 00000000000..948f888cd68 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/cache/ProjectSyncContainerTest.java @@ -0,0 +1,52 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.cache; + +import java.util.HashMap; +import org.junit.Test; +import org.sonar.batch.bootstrap.GlobalProperties; +import org.sonar.core.platform.ComponentContainer; +import org.sonar.home.cache.PersistentCache; +import org.sonar.scanner.protocol.input.ProjectRepositories; +import org.sonarqube.ws.client.WsClient; + +import static org.mockito.Mockito.mock; + +public class ProjectSyncContainerTest { + private ComponentContainer createParentContainer() { + PersistentCache cache = mock(PersistentCache.class); + WsClient server = mock(WsClient.class); + + GlobalProperties globalProps = new GlobalProperties(new HashMap<String, String>()); + ComponentContainer parent = new ComponentContainer(); + parent.add(cache); + parent.add(server); + parent.add(globalProps); + return parent; + } + + @Test + public void testProjectRepository() { + ProjectSyncContainer container = new ProjectSyncContainer(createParentContainer(), "my:project", true); + container.doBeforeStart(); + container.getPicoContainer().start(); + container.getComponentByType(ProjectRepositories.class); + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/cache/StrategyWSLoaderProviderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/cache/StrategyWSLoaderProviderTest.java new file mode 100644 index 00000000000..ce9d88a037c --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/cache/StrategyWSLoaderProviderTest.java @@ -0,0 +1,59 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.cache; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.sonar.batch.bootstrap.BatchWsClient; +import org.sonar.batch.cache.WSLoader.LoadStrategy; +import org.sonar.home.cache.PersistentCache; + +import static org.assertj.core.api.Assertions.assertThat; + +public class StrategyWSLoaderProviderTest { + @Mock + private PersistentCache cache; + + @Mock + private BatchWsClient client; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + } + + @Test + public void testStrategy() { + StrategyWSLoaderProvider provider = new StrategyWSLoaderProvider(LoadStrategy.CACHE_FIRST); + WSLoader wsLoader = provider.provide(cache, client); + + assertThat(wsLoader.getDefaultStrategy()).isEqualTo(LoadStrategy.CACHE_FIRST); + } + + @Test + public void testSingleton() { + StrategyWSLoaderProvider provider = new StrategyWSLoaderProvider(LoadStrategy.CACHE_FIRST); + WSLoader wsLoader = provider.provide(cache, client); + + assertThat(provider.provide(null, null)).isEqualTo(wsLoader); + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/cache/WSLoaderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/cache/WSLoaderTest.java new file mode 100644 index 00000000000..ad7bb763d67 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/cache/WSLoaderTest.java @@ -0,0 +1,264 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.cache; + +import java.io.IOException; +import java.io.InputStream; +import org.apache.commons.io.IOUtils; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.mockito.InOrder; +import org.mockito.Mockito; +import org.sonar.batch.bootstrap.BatchWsClient; +import org.sonar.batch.cache.WSLoader.LoadStrategy; +import org.sonar.home.cache.PersistentCache; +import org.sonarqube.ws.client.HttpException; +import org.sonarqube.ws.client.MockWsResponse; +import org.sonarqube.ws.client.WsRequest; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.fail; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +public class WSLoaderTest { + private final static String ID = "dummy"; + private final static String cacheValue = "cache"; + private final static String serverValue = "server"; + + @Rule + public ExpectedException exception = ExpectedException.none(); + + BatchWsClient ws = mock(BatchWsClient.class, Mockito.RETURNS_DEEP_STUBS); + PersistentCache cache = mock(PersistentCache.class); + + @Test + public void dont_retry_server_offline() throws IOException { + turnServerOffline(); + when(cache.getString(ID)).thenReturn(cacheValue); + WSLoader underTest = new WSLoader(LoadStrategy.SERVER_FIRST, cache, ws); + + assertResult(underTest.loadString(ID), cacheValue, true); + assertResult(underTest.loadString(ID), cacheValue, true); + + assertUsedServer(1); + assertUsedCache(2); + } + + @Test + public void get_stream_from_cache() throws IOException { + InputStream is = IOUtils.toInputStream("is"); + when(cache.getStream(ID)).thenReturn(is); + + WSLoader loader = new WSLoader(LoadStrategy.CACHE_FIRST, cache, ws); + WSLoaderResult<InputStream> result = loader.loadStream(ID); + + assertThat(result.get()).isEqualTo(is); + verify(cache).getStream(ID); + verifyNoMoreInteractions(cache, ws); + } + + @Test + public void put_stream_in_cache() throws IOException { + InputStream input = IOUtils.toInputStream("is"); + + when(ws.call(any(WsRequest.class))).thenReturn(new MockWsResponse().setContent(input)); + when(cache.getStream(ID)).thenReturn(input); + + // SERVER_FIRST -> load from server then put to cache + WSLoader underTest = new WSLoader(LoadStrategy.SERVER_FIRST, cache, ws); + WSLoaderResult<InputStream> result = underTest.loadStream(ID); + assertThat(result.get()).isEqualTo(input); + + InOrder inOrder = inOrder(ws, cache); + inOrder.verify(ws).call(any(WsRequest.class)); + inOrder.verify(cache).put(eq(ID), any(InputStream.class)); + inOrder.verify(cache).getStream(ID); + verifyNoMoreInteractions(cache, ws); + } + + @Test + public void test_cache_strategy_fallback() throws IOException { + turnCacheEmpty(); + when(ws.call(any(WsRequest.class))).thenReturn(new MockWsResponse().setContent(serverValue)); + WSLoader loader = new WSLoader(LoadStrategy.CACHE_FIRST, cache, ws); + + assertResult(loader.loadString(ID), serverValue, false); + + InOrder inOrder = inOrder(ws, cache); + inOrder.verify(cache).getString(ID); + inOrder.verify(ws).call(any(WsRequest.class)); + } + + @Test + public void test_server_strategy_fallback() throws IOException { + turnServerOffline(); + when(cache.getString(ID)).thenReturn(cacheValue); + WSLoader loader = new WSLoader(LoadStrategy.SERVER_FIRST, cache, ws); + + assertResult(loader.loadString(ID), cacheValue, true); + + InOrder inOrder = inOrder(ws, cache); + inOrder.verify(ws).call(any(WsRequest.class)); + inOrder.verify(cache).getString(ID); + } + + @Test + public void test_put_cache() throws IOException { + when(ws.call(any(WsRequest.class))).thenReturn(new MockWsResponse().setContent(serverValue)); + WSLoader loader = new WSLoader(LoadStrategy.SERVER_FIRST, cache, ws); + loader.loadString(ID); + verify(cache).put(ID, serverValue.getBytes()); + } + + @Test + public void test_throw_cache_exception_fallback() throws IOException { + turnServerOffline(); + + when(cache.getString(ID)).thenThrow(new NullPointerException()); + WSLoader loader = new WSLoader(LoadStrategy.SERVER_FIRST, cache, ws); + + try { + loader.loadString(ID); + fail("NPE expected"); + } catch (NullPointerException e) { + assertUsedServer(1); + assertUsedCache(1); + } + } + + @Test + public void test_throw_cache_exception() throws IOException { + when(cache.getString(ID)).thenThrow(new IllegalStateException()); + + WSLoader loader = new WSLoader(LoadStrategy.CACHE_FIRST, cache, ws); + + try { + loader.loadString(ID); + fail("IllegalStateException expected"); + } catch (IllegalStateException e) { + assertUsedServer(0); + assertUsedCache(1); + } + } + + @Test + public void test_throw_http_exceptions() { + when(ws.call(any(WsRequest.class))).thenThrow(new HttpException("url", 500)); + + WSLoader loader = new WSLoader(LoadStrategy.SERVER_FIRST, cache, ws); + + try { + loader.loadString(ID); + fail("IllegalStateException expected"); + } catch (HttpException e) { + // cache should not be used + verifyNoMoreInteractions(cache); + } + } + + @Test + public void test_server_only_not_available() { + turnServerOffline(); + + exception.expect(IllegalStateException.class); + exception.expectMessage("Server is not available"); + + WSLoader loader = new WSLoader(LoadStrategy.SERVER_ONLY, cache, ws); + loader.loadString(ID); + } + + @Test + public void test_server_cache_not_available() throws IOException { + turnServerOffline(); + turnCacheEmpty(); + + exception.expect(IllegalStateException.class); + exception.expectMessage("Server is not accessible and data is not cached"); + + WSLoader loader = new WSLoader(LoadStrategy.SERVER_FIRST, cache, ws); + loader.loadString(ID); + } + + @Test + public void test_cache_only_available() throws IOException { + turnCacheEmpty(); + + exception.expect(IllegalStateException.class); + exception.expectMessage("Data is not cached"); + + WSLoader loader = new WSLoader(LoadStrategy.CACHE_ONLY, cache, ws); + loader.loadString(ID); + } + + @Test + public void test_server_strategy() throws IOException { + when(ws.call(any(WsRequest.class))).thenReturn(new MockWsResponse().setContent(serverValue)); + WSLoader loader = new WSLoader(LoadStrategy.SERVER_FIRST, cache, ws); + assertResult(loader.loadString(ID), serverValue, false); + + // should not fetch from cache + verify(cache).put(ID, serverValue.getBytes()); + verifyNoMoreInteractions(cache); + } + + @Test(expected = IllegalStateException.class) + public void test_server_only() throws IOException { + turnServerOffline(); + WSLoader loader = new WSLoader(LoadStrategy.SERVER_ONLY, cache, ws); + loader.loadString(ID); + } + + @Test + public void test_string() { + when(ws.call(any(WsRequest.class))).thenReturn(new MockWsResponse().setContent(serverValue)); + WSLoader loader = new WSLoader(LoadStrategy.SERVER_FIRST, cache, ws); + assertResult(loader.loadString(ID), serverValue, false); + } + + private void assertUsedCache(int times) throws IOException { + verify(cache, times(times)).getString(ID); + } + + private void assertUsedServer(int times) { + verify(ws, times(times)).call(any(WsRequest.class)); + } + + private void assertResult(WSLoaderResult<String> result, String expected, boolean fromCache) { + assertThat(result).isNotNull(); + assertThat(result.get()).isEqualTo(expected); + assertThat(result.isFromCache()).isEqualTo(fromCache); + } + + private void turnServerOffline() { + when(ws.call(any(WsRequest.class))).thenThrow(new IllegalStateException()); + } + + private void turnCacheEmpty() throws IOException { + when(cache.getString(ID)).thenReturn(null); + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/cpd/CpdComponentsTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/cpd/CpdComponentsTest.java new file mode 100644 index 00000000000..814e7d69d38 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/cpd/CpdComponentsTest.java @@ -0,0 +1,32 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.cpd; + +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class CpdComponentsTest { + + @Test + public void getExtensions() { + assertThat(CpdComponents.all().size()).isGreaterThan(0); + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/cpd/CpdExecutorTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/cpd/CpdExecutorTest.java new file mode 100644 index 00000000000..6a6c209348f --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/cpd/CpdExecutorTest.java @@ -0,0 +1,239 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.cpd; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.fs.internal.DefaultInputModule; +import org.sonar.api.config.Settings; +import org.sonar.api.resources.Project; +import org.sonar.api.utils.log.LogTester; +import org.sonar.api.utils.log.LoggerLevel; +import org.sonar.batch.cpd.index.SonarCpdBlockIndex; +import org.sonar.batch.index.BatchComponent; +import org.sonar.batch.index.BatchComponentCache; +import org.sonar.batch.report.ReportPublisher; +import org.sonar.core.util.CloseableIterator; +import org.sonar.duplications.index.CloneGroup; +import org.sonar.duplications.index.ClonePart; +import org.sonar.scanner.protocol.output.ScannerReportReader; +import org.sonar.scanner.protocol.output.ScannerReportWriter; +import org.sonar.scanner.protocol.output.ScannerReport.Duplicate; +import org.sonar.scanner.protocol.output.ScannerReport.Duplication; +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class CpdExecutorTest { + private CpdExecutor executor; + private Settings settings; + private SonarCpdBlockIndex index; + private ReportPublisher publisher; + private BatchComponentCache componentCache; + + @Rule + public LogTester logTester = new LogTester(); + + @Rule + public TemporaryFolder temp = new TemporaryFolder(); + + // private AbstractCpdEngine engine; + + private ScannerReportReader reader; + private BatchComponent batchComponent1; + private BatchComponent batchComponent2; + private BatchComponent batchComponent3; + + @Before + public void setUp() throws IOException { + File outputDir = temp.newFolder(); + + settings = new Settings(); + index = mock(SonarCpdBlockIndex.class); + publisher = mock(ReportPublisher.class); + when(publisher.getWriter()).thenReturn(new ScannerReportWriter(outputDir)); + componentCache = new BatchComponentCache(); + executor = new CpdExecutor(settings, index, publisher, componentCache); + reader = new ScannerReportReader(outputDir); + + Project p = new Project("foo"); + componentCache.add(p, null).setInputComponent(new DefaultInputModule("foo")); + + batchComponent1 = createComponent("src/Foo.php", 5); + batchComponent2 = createComponent("src/Foo2.php", 5); + batchComponent3 = createComponent("src/Foo3.php", 5); + } + + private BatchComponent createComponent(String relativePath, int lines) { + org.sonar.api.resources.Resource sampleFile = org.sonar.api.resources.File.create("relativePath").setEffectiveKey("foo:" + relativePath); + return componentCache.add(sampleFile, null).setInputComponent(new DefaultInputFile("foo", relativePath).setLines(lines)); + } + + @Test + public void defaultMinimumTokens() { + assertThat(executor.getMinimumTokens("java")).isEqualTo(100); + } + + @Test + public void minimumTokensByLanguage() { + settings.setProperty("sonar.cpd.java.minimumTokens", "42"); + settings.setProperty("sonar.cpd.php.minimumTokens", "33"); + assertThat(executor.getMinimumTokens("java")).isEqualTo(42); + + settings.setProperty("sonar.cpd.java.minimumTokens", "42"); + settings.setProperty("sonar.cpd.php.minimumTokens", "33"); + assertThat(executor.getMinimumTokens("php")).isEqualTo(33); + } + + @Test + public void testNothingToSave() { + executor.saveDuplications(batchComponent1, Collections.<CloneGroup>emptyList()); + assertThat(reader.readComponentDuplications(batchComponent1.batchId())).hasSize(0); + } + + @Test + public void reportOneSimpleDuplicationBetweenTwoFiles() { + List<CloneGroup> groups = Arrays.asList(newCloneGroup(new ClonePart(batchComponent1.key(), 0, 2, 4), new ClonePart(batchComponent2.key(), 0, 15, 17))); + + executor.saveDuplications(batchComponent1, groups); + + Duplication[] dups = readDuplications(1); + assertDuplication(dups[0], 2, 4, batchComponent2.batchId(), 15, 17); + } + + @Test + public void reportDuplicationOnSameFile() throws Exception { + List<CloneGroup> groups = Arrays.asList(newCloneGroup(new ClonePart(batchComponent1.key(), 0, 5, 204), new ClonePart(batchComponent1.key(), 0, 215, 414))); + executor.saveDuplications(batchComponent1, groups); + + Duplication[] dups = readDuplications(1); + assertDuplication(dups[0], 5, 204, null, 215, 414); + } + + @Test + public void reportTooManyDuplicates() throws Exception { + // 1 origin part + 101 duplicates = 102 + List<ClonePart> parts = new ArrayList<>(CpdExecutor.MAX_CLONE_PART_PER_GROUP + 2); + for (int i = 0; i < CpdExecutor.MAX_CLONE_PART_PER_GROUP + 2; i++) { + parts.add(new ClonePart(batchComponent1.key(), i, i, i + 1)); + } + List<CloneGroup> groups = Arrays.asList(CloneGroup.builder().setLength(0).setOrigin(parts.get(0)).setParts(parts).build()); + executor.saveDuplications(batchComponent1, groups); + + Duplication[] dups = readDuplications(1); + assertThat(dups[0].getDuplicateList()).hasSize(CpdExecutor.MAX_CLONE_PART_PER_GROUP); + + assertThat(logTester.logs(LoggerLevel.WARN)) + .contains("Too many duplication references on file " + batchComponent1.inputComponent() + " for block at line 0. Keep only the first " + + CpdExecutor.MAX_CLONE_PART_PER_GROUP + " references."); + } + + @Test + public void reportTooManyDuplications() throws Exception { + // 1 origin part + 101 duplicates = 102 + List<CloneGroup> dups = new ArrayList<>(CpdExecutor.MAX_CLONE_GROUP_PER_FILE + 1); + for (int i = 0; i < CpdExecutor.MAX_CLONE_GROUP_PER_FILE + 1; i++) { + ClonePart clonePart = new ClonePart(batchComponent1.key(), i, i, i + 1); + ClonePart dupPart = new ClonePart(batchComponent1.key(), i + 1, i + 1, i + 2); + dups.add(newCloneGroup(clonePart, dupPart)); + } + executor.saveDuplications(batchComponent1, dups); + + assertThat(reader.readComponentDuplications(batchComponent1.batchId())).hasSize(CpdExecutor.MAX_CLONE_GROUP_PER_FILE); + + assertThat(logTester.logs(LoggerLevel.WARN)) + .contains("Too many duplication groups on file " + batchComponent1.inputComponent() + ". Keep only the first " + CpdExecutor.MAX_CLONE_GROUP_PER_FILE + " groups."); + } + + @Test + public void reportOneDuplicatedGroupInvolvingMoreThanTwoFiles() throws Exception { + List<CloneGroup> groups = Arrays + .asList(newCloneGroup(new ClonePart(batchComponent1.key(), 0, 5, 204), new ClonePart(batchComponent2.key(), 0, 15, 214), new ClonePart(batchComponent3.key(), 0, 25, 224))); + executor.saveDuplications(batchComponent1, groups); + + Duplication[] dups = readDuplications(1); + assertDuplication(dups[0], 5, 204, 2); + assertDuplicate(dups[0].getDuplicate(0), batchComponent2.batchId(), 15, 214); + assertDuplicate(dups[0].getDuplicate(1), batchComponent3.batchId(), 25, 224); + } + + @Test + public void reportTwoDuplicatedGroupsInvolvingThreeFiles() throws Exception { + List<CloneGroup> groups = Arrays.asList( + newCloneGroup(new ClonePart(batchComponent1.key(), 0, 5, 204), new ClonePart(batchComponent2.key(), 0, 15, 214)), + newCloneGroup(new ClonePart(batchComponent1.key(), 0, 15, 214), new ClonePart(batchComponent3.key(), 0, 15, 214))); + executor.saveDuplications(batchComponent1, groups); + + Duplication[] dups = readDuplications(2); + assertDuplication(dups[0], 5, 204, batchComponent2.batchId(), 15, 214); + assertDuplication(dups[1], 15, 214, batchComponent3.batchId(), 15, 214); + } + + private Duplication[] readDuplications(int expected) { + assertThat(reader.readComponentDuplications(batchComponent1.batchId())).hasSize(expected); + Duplication[] duplications = new Duplication[expected]; + CloseableIterator<Duplication> dups = reader.readComponentDuplications(batchComponent1.batchId()); + + for(int i = 0; i< expected; i++) { + duplications[i] = dups.next(); + } + dups.close(); + return duplications; + } + + private void assertDuplicate(Duplicate d, int otherFileRef, int rangeStartLine, int rangeEndLine) { + assertThat(d.getOtherFileRef()).isEqualTo(otherFileRef); + assertThat(d.getRange().getStartLine()).isEqualTo(rangeStartLine); + assertThat(d.getRange().getEndLine()).isEqualTo(rangeEndLine); + } + + private void assertDuplication(Duplication d, int originStartLine, int originEndLine, int numDuplicates) { + assertThat(d.getOriginPosition().getStartLine()).isEqualTo(originStartLine); + assertThat(d.getOriginPosition().getEndLine()).isEqualTo(originEndLine); + assertThat(d.getDuplicateList()).hasSize(numDuplicates); + } + + private void assertDuplication(Duplication d, int originStartLine, int originEndLine, Integer otherFileRef, int rangeStartLine, int rangeEndLine) { + assertThat(d.getOriginPosition().getStartLine()).isEqualTo(originStartLine); + assertThat(d.getOriginPosition().getEndLine()).isEqualTo(originEndLine); + assertThat(d.getDuplicateList()).hasSize(1); + if(otherFileRef != null) { + assertThat(d.getDuplicate(0).getOtherFileRef()).isEqualTo(otherFileRef); + } else { + assertThat(d.getDuplicate(0).hasOtherFileRef()).isFalse(); + } + assertThat(d.getDuplicate(0).getRange().getStartLine()).isEqualTo(rangeStartLine); + assertThat(d.getDuplicate(0).getRange().getEndLine()).isEqualTo(rangeEndLine); + } + + private CloneGroup newCloneGroup(ClonePart... parts) { + return CloneGroup.builder().setLength(0).setOrigin(parts[0]).setParts(Arrays.asList(parts)).build(); + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/cpd/CpdSensorTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/cpd/CpdSensorTest.java new file mode 100644 index 00000000000..ce27f0776c4 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/cpd/CpdSensorTest.java @@ -0,0 +1,80 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.cpd; + +import java.io.IOException; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.sonar.api.batch.fs.internal.DefaultFileSystem; +import org.sonar.api.config.PropertyDefinitions; +import org.sonar.api.config.Settings; +import org.sonar.api.resources.Java; + +import static org.assertj.core.api.Assertions.assertThat; + +public class CpdSensorTest { + + @Rule + public TemporaryFolder temp = new TemporaryFolder(); + + JavaCpdBlockIndexer sonarEngine; + DefaultCpdBlockIndexer sonarBridgeEngine; + CpdSensor sensor; + Settings settings; + + @Before + public void setUp() throws IOException { + sonarEngine = new JavaCpdBlockIndexer(null, null, null); + sonarBridgeEngine = new DefaultCpdBlockIndexer(new CpdMappings(), null, null, null); + settings = new Settings(new PropertyDefinitions(CpdComponents.class)); + + DefaultFileSystem fs = new DefaultFileSystem(temp.newFolder().toPath()); + sensor = new CpdSensor(sonarEngine, sonarBridgeEngine, settings, fs); + } + + @Test + public void test_global_skip() { + settings.setProperty("sonar.cpd.skip", true); + assertThat(sensor.isSkipped(Java.KEY)).isTrue(); + } + + @Test + public void should_not_skip_by_default() { + assertThat(sensor.isSkipped(Java.KEY)).isFalse(); + } + + @Test + public void should_skip_by_language() { + settings.setProperty("sonar.cpd.skip", false); + settings.setProperty("sonar.cpd.php.skip", true); + + assertThat(sensor.isSkipped("php")).isTrue(); + assertThat(sensor.isSkipped(Java.KEY)).isFalse(); + } + + @Test + public void test_engine() { + assertThat(sensor.getEngine(Java.KEY)).isSameAs(sonarEngine); + assertThat(sensor.getEngine("PHP")).isSameAs(sonarBridgeEngine); + } + +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/cpd/DefaultCpdBlockIndexerTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/cpd/DefaultCpdBlockIndexerTest.java new file mode 100644 index 00000000000..9fcd03ac940 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/cpd/DefaultCpdBlockIndexerTest.java @@ -0,0 +1,80 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.cpd; + +import org.junit.Before; +import org.junit.Test; +import org.slf4j.Logger; +import org.sonar.api.config.Settings; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +public class DefaultCpdBlockIndexerTest { + + private DefaultCpdBlockIndexer engine; + private Settings settings; + + @Before + public void init() { + settings = new Settings(); + engine = new DefaultCpdBlockIndexer(null, null, settings, null); + } + + @Test + public void shouldLogExclusions() { + Logger logger = mock(Logger.class); + engine.logExclusions(new String[0], logger); + verify(logger, never()).info(anyString()); + + logger = mock(Logger.class); + engine.logExclusions(new String[] {"Foo*", "**/Bar*"}, logger); + + String message = "Copy-paste detection exclusions:" + + "\n Foo*" + + "\n **/Bar*"; + verify(logger, times(1)).info(message); + } + + @Test + public void shouldReturnDefaultBlockSize() { + assertThat(DefaultCpdBlockIndexer.getDefaultBlockSize("cobol")).isEqualTo(30); + assertThat(DefaultCpdBlockIndexer.getDefaultBlockSize("abap")).isEqualTo(20); + assertThat(DefaultCpdBlockIndexer.getDefaultBlockSize("other")).isEqualTo(10); + } + + @Test + public void defaultBlockSize() { + + assertThat(engine.getBlockSize("java")).isEqualTo(10); + } + + @Test + public void blockSizeForCobol() { + settings.setProperty("sonar.cpd.cobol.minimumLines", "42"); + + assertThat(engine.getBlockSize("cobol")).isEqualTo(42); + } + +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/cpd/DuplicationPredicatesTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/cpd/DuplicationPredicatesTest.java new file mode 100644 index 00000000000..6b6fa4fa0e8 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/cpd/DuplicationPredicatesTest.java @@ -0,0 +1,38 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.cpd; + +import com.google.common.base.Predicate; +import org.junit.Test; +import org.sonar.duplications.index.CloneGroup; + +import static org.assertj.core.api.Assertions.assertThat; + +public class DuplicationPredicatesTest { + + @Test + public void testNumberOfUnitsNotLessThan() { + Predicate<CloneGroup> predicate = DuplicationPredicates.numberOfUnitsNotLessThan(5); + assertThat(predicate.apply(CloneGroup.builder().setLengthInUnits(6).build())).isTrue(); + assertThat(predicate.apply(CloneGroup.builder().setLengthInUnits(5).build())).isTrue(); + assertThat(predicate.apply(CloneGroup.builder().setLengthInUnits(4).build())).isFalse(); + } + +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/cpd/JavaCpdBlockIndexerTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/cpd/JavaCpdBlockIndexerTest.java new file mode 100644 index 00000000000..851fb523815 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/cpd/JavaCpdBlockIndexerTest.java @@ -0,0 +1,106 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.cpd; + +import org.apache.commons.io.FileUtils; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.sonar.api.CoreProperties; +import org.sonar.api.batch.fs.FileSystem; +import org.sonar.api.batch.fs.internal.DefaultFileSystem; +import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.config.Settings; +import org.sonar.batch.cpd.index.SonarCpdBlockIndex; +import org.sonar.batch.index.BatchComponentCache; +import org.sonar.duplications.block.Block; + +import java.io.File; +import java.io.IOException; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; + +public class JavaCpdBlockIndexerTest { + private static final String JAVA = "java"; + + @Mock + private SonarCpdBlockIndex index; + + @Captor + private ArgumentCaptor<List<Block>> blockCaptor; + + private Settings settings; + private JavaCpdBlockIndexer engine; + private DefaultInputFile file; + + @Rule + public TemporaryFolder temp = new TemporaryFolder(); + + @Before + public void setUp() throws IOException { + MockitoAnnotations.initMocks(this); + + File baseDir = temp.newFolder(); + DefaultFileSystem fs = new DefaultFileSystem(baseDir); + file = new DefaultInputFile("foo", "src/ManyStatements.java").setLanguage(JAVA); + fs.add(file); + BatchComponentCache batchComponentCache = new BatchComponentCache(); + batchComponentCache.add(org.sonar.api.resources.File.create("src/Foo.java").setEffectiveKey("foo:src/ManyStatements.java"), null).setInputComponent(file); + File ioFile = file.file(); + FileUtils.copyURLToFile(this.getClass().getResource("ManyStatements.java"), ioFile); + + settings = new Settings(); + engine = new JavaCpdBlockIndexer(fs, settings, index); + } + + @Test + public void languageSupported() { + JavaCpdBlockIndexer engine = new JavaCpdBlockIndexer(mock(FileSystem.class), new Settings(), index); + assertThat(engine.isLanguageSupported(JAVA)).isTrue(); + assertThat(engine.isLanguageSupported("php")).isFalse(); + } + + @Test + public void testExclusions() { + settings.setProperty(CoreProperties.CPD_EXCLUSIONS, "**"); + engine.index(JAVA); + verifyZeroInteractions(index); + } + + @Test + public void testJavaIndexing() throws Exception { + engine.index(JAVA); + + verify(index).insert(eq(file), blockCaptor.capture()); + List<Block> blockList = blockCaptor.getValue(); + + assertThat(blockList).hasSize(26); + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/deprecated/perspectives/PerspectiveBuilderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/deprecated/perspectives/PerspectiveBuilderTest.java new file mode 100644 index 00000000000..b07671362ce --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/deprecated/perspectives/PerspectiveBuilderTest.java @@ -0,0 +1,44 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.deprecated.perspectives; + +import org.junit.Test; +import org.sonar.api.component.Perspective; +import org.sonar.batch.index.BatchComponent; + +import static org.assertj.core.api.Assertions.assertThat; + +public class PerspectiveBuilderTest { + @Test + public void testGetPerspectiveClass() throws Exception { + PerspectiveBuilder<FakePerspective> builder = new PerspectiveBuilder<FakePerspective>(FakePerspective.class) { + @Override + public FakePerspective loadPerspective(Class<FakePerspective> perspectiveClass, BatchComponent component) { + return null; + } + }; + + assertThat(builder.getPerspectiveClass()).isEqualTo(FakePerspective.class); + } + + static interface FakePerspective extends Perspective { + + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/events/BatchStepEventTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/events/BatchStepEventTest.java new file mode 100644 index 00000000000..3f3738b3900 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/events/BatchStepEventTest.java @@ -0,0 +1,45 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.events; + +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +public class BatchStepEventTest { + + private BatchStepEvent batchStepEvent = new BatchStepEvent("foo", true); + + @Test + public void testGetType() { + assertThat(batchStepEvent.getType()).isEqualTo(BatchStepHandler.class); + } + + @Test + public void testDispatch() { + BatchStepHandler handler = mock(BatchStepHandler.class); + batchStepEvent.dispatch(handler); + + verify(handler).onBatchStep(batchStepEvent); + } + +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/events/EventBusTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/events/EventBusTest.java new file mode 100644 index 00000000000..78fcd4926c2 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/events/EventBusTest.java @@ -0,0 +1,77 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.events; + +import org.junit.Test; +import org.sonar.api.batch.events.EventHandler; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +public class EventBusTest { + + @Test + public void shouldNotifyAboutEvent() { + FirstHandler firstHandler = mock(FirstHandler.class); + SecondHandler secondHandler = mock(SecondHandler.class); + EventBus eventBus = new EventBus(new EventHandler[] { firstHandler, secondHandler }); + + FirstEvent firstEvent = new FirstEvent(); + eventBus.fireEvent(firstEvent); + SecondEvent secondEvent = new SecondEvent(); + eventBus.fireEvent(secondEvent); + + verify(firstHandler).onEvent(firstEvent); + verify(secondHandler).onEvent(secondEvent); + } + + interface FirstHandler extends EventHandler { + void onEvent(FirstEvent event); + } + + static class FirstEvent extends BatchEvent<FirstHandler> { + @Override + protected void dispatch(FirstHandler handler) { + handler.onEvent(this); + } + + @Override + public Class getType() { + return FirstHandler.class; + } + } + + interface SecondHandler extends EventHandler { + void onEvent(SecondEvent event); + } + + static class SecondEvent extends BatchEvent<SecondHandler> { + @Override + protected void dispatch(SecondHandler handler) { + handler.onEvent(this); + } + + @Override + public Class getType() { + return SecondHandler.class; + } + } + +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/index/AbstractCachesTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/index/AbstractCachesTest.java new file mode 100644 index 00000000000..b3e6592d9d2 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/index/AbstractCachesTest.java @@ -0,0 +1,77 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.index; + +import org.junit.After; + +import org.junit.Before; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import com.google.common.collect.ImmutableMap; +import org.sonar.api.CoreProperties; +import org.sonar.batch.bootstrap.GlobalProperties; +import org.sonar.batch.bootstrap.GlobalTempFolderProvider; + +import java.util.Map; + +import org.junit.ClassRule; +import org.junit.rules.TemporaryFolder; + +public abstract class AbstractCachesTest { + @ClassRule + public static TemporaryFolder temp = new TemporaryFolder(); + + protected static CachesManager cachesManager; + protected Caches caches; + + private static CachesManager createCacheOnTemp() { + Map<String, String> props = ImmutableMap.of(CoreProperties.WORKING_DIRECTORY, temp.getRoot().getAbsolutePath(), + CoreProperties.GLOBAL_WORKING_DIRECTORY, temp.getRoot().getAbsolutePath()); + + return new CachesManager(new GlobalTempFolderProvider().provide(new GlobalProperties(props))); + } + + @BeforeClass + public static void startClass() { + cachesManager = createCacheOnTemp(); + cachesManager.start(); + } + + @Before + public void start() { + caches = new Caches(cachesManager); + caches.start(); + } + + @After + public void stop() { + if (caches != null) { + caches.stop(); + caches = null; + } + } + + @AfterClass + public static void stopClass() { + if (cachesManager != null) { + cachesManager.stop(); + } + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/index/BatchComponentCacheTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/index/BatchComponentCacheTest.java new file mode 100644 index 00000000000..8d014b67321 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/index/BatchComponentCacheTest.java @@ -0,0 +1,53 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.index; + +import org.junit.Test; +import org.sonar.api.resources.File; +import org.sonar.api.resources.Resource; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.fail; + +public class BatchComponentCacheTest { + @Test + public void should_cache_resource() { + BatchComponentCache cache = new BatchComponentCache(); + String componentKey = "struts:src/org/struts/Action.java"; + Resource resource = File.create("org/struts/Action.java").setEffectiveKey(componentKey); + cache.add(resource, null); + + assertThat(cache.get(componentKey).resource()).isSameAs(resource); + assertThat(cache.get("other")).isNull(); + } + + @Test + public void should_fail_if_missing_component_key() { + BatchComponentCache cache = new BatchComponentCache(); + Resource resource = File.create("org/struts/Action.java").setEffectiveKey(null); + try { + cache.add(resource, null); + fail(); + } catch (IllegalStateException e) { + // success + assertThat(e).hasMessage("Missing resource effective key"); + } + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/index/BucketTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/index/BucketTest.java new file mode 100644 index 00000000000..a6a5f900088 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/index/BucketTest.java @@ -0,0 +1,58 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.index; + +import org.junit.Test; +import org.sonar.api.measures.Metric; +import org.sonar.api.resources.Directory; +import org.sonar.api.resources.File; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; + +public class BucketTest { + + Directory directory = Directory.create("org/foo"); + File javaFile = File.create("org/foo/Bar.java"); + Metric ncloc = new Metric("ncloc"); + + @Test + public void shouldManageRelationships() { + Bucket packageBucket = new Bucket(directory); + Bucket fileBucket = new Bucket(javaFile); + fileBucket.setParent(packageBucket); + + assertThat(fileBucket.getParent()).isEqualTo(packageBucket); + assertThat(packageBucket.getChildren()).containsExactly(fileBucket); + } + + @Test + public void shouldBeEquals() { + assertEquals(new Bucket(directory), new Bucket(directory)); + assertEquals(new Bucket(directory).hashCode(), new Bucket(directory).hashCode()); + } + + @Test + public void shouldNotBeEquals() { + assertFalse(new Bucket(directory).equals(new Bucket(javaFile))); + assertThat(new Bucket(directory).hashCode()).isNotEqualTo(new Bucket(javaFile).hashCode()); + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/index/CacheTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/index/CacheTest.java new file mode 100644 index 00000000000..95ddcb8b766 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/index/CacheTest.java @@ -0,0 +1,250 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.index; + +import com.google.common.collect.Iterables; +import org.junit.Test; +import org.sonar.batch.index.Cache.Entry; + +import static org.assertj.core.api.Assertions.assertThat; + +public class CacheTest extends AbstractCachesTest { + + @Test + public void one_part_key() { + Cache<String> cache = caches.createCache("capitals"); + + assertThat(cache.get("france")).isNull(); + + cache.put("france", "paris"); + cache.put("italy", "rome"); + assertThat(cache.get("france")).isEqualTo("paris"); + assertThat(cache.keySet()).containsOnly("france", "italy"); + assertThat(cache.keySet("france")).isEmpty(); + Iterable<String> values = cache.values(); + assertThat(values).containsOnly("paris", "rome"); + assertThat(values).containsOnly("paris", "rome"); + assertThat(cache.containsKey("france")).isTrue(); + + Iterable<Entry<String>> iterable = cache.entries(); + Cache.Entry[] entries = Iterables.toArray(iterable, Cache.Entry.class); + assertThat(entries).hasSize(2); + assertThat(iterable).hasSize(2); + assertThat(entries[0].key()[0]).isEqualTo("france"); + assertThat(entries[0].value()).isEqualTo("paris"); + assertThat(entries[1].key()[0]).isEqualTo("italy"); + assertThat(entries[1].value()).isEqualTo("rome"); + + cache.remove("france"); + assertThat(cache.get("france")).isNull(); + assertThat(cache.get("italy")).isEqualTo("rome"); + assertThat(cache.keySet()).containsOnly("italy"); + assertThat(cache.keySet("france")).isEmpty(); + assertThat(cache.containsKey("france")).isFalse(); + assertThat(cache.containsKey("italy")).isTrue(); + assertThat(values).containsOnly("rome"); + + cache.clear(); + assertThat(values).isEmpty(); + } + + @Test + public void test_key_being_prefix_of_another_key() throws Exception { + Cache<String> cache = caches.createCache("components"); + + cache.put("struts-el:org.apache.strutsel.taglib.html.ELButtonTag", "the Tag"); + cache.put("struts-el:org.apache.strutsel.taglib.html.ELButtonTagBeanInfo", "the BeanInfo"); + + assertThat(cache.get("struts-el:org.apache.strutsel.taglib.html.ELButtonTag")).isEqualTo("the Tag"); + assertThat(cache.get("struts-el:org.apache.strutsel.taglib.html.ELButtonTagBeanInfo")).isEqualTo("the BeanInfo"); + } + + @Test + public void two_parts_key() { + Cache<String> cache = caches.createCache("capitals"); + + assertThat(cache.get("europe", "france")).isNull(); + + cache.put("europe", "france", "paris"); + cache.put("europe", "italy", "rome"); + cache.put("asia", "china", "pekin"); + assertThat(cache.get("europe")).isNull(); + assertThat(cache.get("europe", "france")).isEqualTo("paris"); + assertThat(cache.get("europe", "italy")).isEqualTo("rome"); + assertThat(cache.get("europe")).isNull(); + assertThat(cache.keySet("europe")).containsOnly("france", "italy"); + assertThat(cache.keySet()).containsOnly("europe", "asia"); + assertThat(cache.containsKey("europe")).isFalse(); + assertThat(cache.containsKey("europe", "france")).isTrue(); + assertThat(cache.containsKey("europe", "spain")).isFalse(); + assertThat(cache.values()).containsOnly("paris", "rome", "pekin"); + assertThat(cache.values("america")).isEmpty(); + assertThat(cache.values("europe")).containsOnly("paris", "rome"); + assertThat(cache.values("oceania")).isEmpty(); + + Iterable<Entry<String>> iterable = cache.entries(); + Cache.Entry[] allEntries = Iterables.toArray(iterable, Cache.Entry.class); + assertThat(allEntries).hasSize(3); + assertThat(iterable).hasSize(3); + assertThat(allEntries[0].key()).isEqualTo(new String[] {"asia", "china"}); + assertThat(allEntries[0].value()).isEqualTo("pekin"); + assertThat(allEntries[1].key()).isEqualTo(new String[] {"europe", "france"}); + assertThat(allEntries[1].value()).isEqualTo("paris"); + assertThat(allEntries[2].key()).isEqualTo(new String[] {"europe", "italy"}); + assertThat(allEntries[2].value()).isEqualTo("rome"); + + Iterable<Entry<String>> iterable2 = cache.entries("europe"); + Cache.Entry[] subEntries = Iterables.toArray(iterable2, Cache.Entry.class); + assertThat(subEntries).hasSize(2); + assertThat(iterable2).hasSize(2); + assertThat(subEntries[0].key()).isEqualTo(new String[] {"europe", "france"}); + assertThat(subEntries[0].value()).isEqualTo("paris"); + assertThat(subEntries[1].key()).isEqualTo(new String[] {"europe", "italy"}); + assertThat(subEntries[1].value()).isEqualTo("rome"); + + cache.remove("europe", "france"); + assertThat(cache.values()).containsOnly("rome", "pekin"); + assertThat(cache.get("europe", "france")).isNull(); + assertThat(cache.get("europe", "italy")).isEqualTo("rome"); + assertThat(cache.containsKey("europe", "france")).isFalse(); + assertThat(cache.keySet("europe")).containsOnly("italy"); + + cache.clear("america"); + assertThat(cache.keySet()).containsOnly("europe", "asia"); + cache.clear(); + assertThat(cache.keySet()).isEmpty(); + } + + @Test + public void three_parts_key() { + Cache<String> cache = caches.createCache("places"); + assertThat(cache.get("europe", "france", "paris")).isNull(); + + cache.put("europe", "france", "paris", "eiffel tower"); + cache.put("europe", "france", "annecy", "lake"); + cache.put("europe", "france", "poitiers", "notre dame"); + cache.put("europe", "italy", "rome", "colosseum"); + cache.put("europe2", "ukrania", "kiev", "dunno"); + cache.put("asia", "china", "pekin", "great wall"); + cache.put("america", "us", "new york", "empire state building"); + assertThat(cache.get("europe")).isNull(); + assertThat(cache.get("europe", "france")).isNull(); + assertThat(cache.get("europe", "france", "paris")).isEqualTo("eiffel tower"); + assertThat(cache.get("europe", "france", "annecy")).isEqualTo("lake"); + assertThat(cache.get("europe", "italy", "rome")).isEqualTo("colosseum"); + assertThat(cache.keySet()).containsOnly("europe", "asia", "america", "europe2"); + assertThat(cache.keySet("europe")).containsOnly("france", "italy"); + assertThat(cache.keySet("europe", "france")).containsOnly("annecy", "paris", "poitiers"); + assertThat(cache.containsKey("europe")).isFalse(); + assertThat(cache.containsKey("europe", "france")).isFalse(); + assertThat(cache.containsKey("europe", "france", "annecy")).isTrue(); + assertThat(cache.containsKey("europe", "france", "biarritz")).isFalse(); + assertThat(cache.values()).containsOnly("eiffel tower", "lake", "colosseum", "notre dame", "great wall", "empire state building", "dunno"); + assertThat(cache.values("europe")).containsOnly("eiffel tower", "lake", "colosseum", "notre dame"); + assertThat(cache.values("europe", "france")).containsOnly("eiffel tower", "lake", "notre dame"); + + Iterable<Entry<String>> iterable = cache.entries(); + Cache.Entry[] allEntries = Iterables.toArray(iterable, Cache.Entry.class); + assertThat(allEntries).hasSize(7); + assertThat(iterable).hasSize(7); + assertThat(allEntries[0].key()).isEqualTo(new String[] {"america", "us", "new york"}); + assertThat(allEntries[0].value()).isEqualTo("empire state building"); + assertThat(allEntries[1].key()).isEqualTo(new String[] {"asia", "china", "pekin"}); + assertThat(allEntries[1].value()).isEqualTo("great wall"); + assertThat(allEntries[2].key()).isEqualTo(new String[] {"europe", "france", "annecy"}); + assertThat(allEntries[2].value()).isEqualTo("lake"); + assertThat(allEntries[3].key()).isEqualTo(new String[] {"europe", "france", "paris"}); + assertThat(allEntries[3].value()).isEqualTo("eiffel tower"); + assertThat(allEntries[4].key()).isEqualTo(new String[] {"europe", "france", "poitiers"}); + assertThat(allEntries[4].value()).isEqualTo("notre dame"); + assertThat(allEntries[5].key()).isEqualTo(new String[] {"europe", "italy", "rome"}); + assertThat(allEntries[5].value()).isEqualTo("colosseum"); + + Iterable<Entry<String>> iterable2 = cache.entries("europe"); + Cache.Entry[] subEntries = Iterables.toArray(iterable2, Cache.Entry.class); + assertThat(subEntries).hasSize(4); + assertThat(iterable2).hasSize(4); + assertThat(subEntries[0].key()).isEqualTo(new String[] {"europe", "france", "annecy"}); + assertThat(subEntries[0].value()).isEqualTo("lake"); + assertThat(subEntries[1].key()).isEqualTo(new String[] {"europe", "france", "paris"}); + assertThat(subEntries[1].value()).isEqualTo("eiffel tower"); + assertThat(subEntries[2].key()).isEqualTo(new String[] {"europe", "france", "poitiers"}); + assertThat(subEntries[2].value()).isEqualTo("notre dame"); + assertThat(subEntries[3].key()).isEqualTo(new String[] {"europe", "italy", "rome"}); + assertThat(subEntries[3].value()).isEqualTo("colosseum"); + + cache.remove("europe", "france", "annecy"); + assertThat(cache.values()).containsOnly("eiffel tower", "colosseum", "notre dame", "great wall", "empire state building", "dunno"); + assertThat(cache.values("europe")).containsOnly("eiffel tower", "colosseum", "notre dame"); + assertThat(cache.values("europe", "france")).containsOnly("eiffel tower", "notre dame"); + assertThat(cache.get("europe", "france", "annecy")).isNull(); + assertThat(cache.get("europe", "italy", "rome")).isEqualTo("colosseum"); + assertThat(cache.containsKey("europe", "france")).isFalse(); + + cache.clear("europe", "italy"); + assertThat(cache.values()).containsOnly("eiffel tower", "notre dame", "great wall", "empire state building", "dunno"); + + cache.clear("europe"); + assertThat(cache.values()).containsOnly("great wall", "empire state building", "dunno"); + + cache.clear(); + assertThat(cache.values()).isEmpty(); + } + + @Test + public void remove_versus_clear() { + Cache<String> cache = caches.createCache("capitals"); + cache.put("europe", "france", "paris"); + cache.put("europe", "italy", "rome"); + + // remove("europe") does not remove sub-keys + cache.remove("europe"); + assertThat(cache.values()).containsOnly("paris", "rome"); + + // clear("europe") removes sub-keys + cache.clear("europe"); + assertThat(cache.values()).isEmpty(); + } + + @Test + public void empty_cache() { + Cache<String> cache = caches.createCache("empty"); + + assertThat(cache.get("foo")).isNull(); + assertThat(cache.get("foo", "bar")).isNull(); + assertThat(cache.get("foo", "bar", "baz")).isNull(); + assertThat(cache.keySet()).isEmpty(); + assertThat(cache.keySet("foo")).isEmpty(); + assertThat(cache.containsKey("foo")).isFalse(); + assertThat(cache.containsKey("foo", "bar")).isFalse(); + assertThat(cache.containsKey("foo", "bar", "baz")).isFalse(); + assertThat(cache.values()).isEmpty(); + assertThat(cache.values("foo")).isEmpty(); + + // do not fail + cache.remove("foo"); + cache.remove("foo", "bar"); + cache.remove("foo", "bar", "baz"); + cache.clear("foo"); + cache.clear("foo", "bar"); + cache.clear("foo", "bar", "baz"); + cache.clear(); + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/index/CachesManagerTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/index/CachesManagerTest.java new file mode 100644 index 00000000000..177c6f1e357 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/index/CachesManagerTest.java @@ -0,0 +1,42 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.index; + +import org.junit.Test; + +import java.io.File; + +import static org.assertj.core.api.Assertions.assertThat; + +public class CachesManagerTest extends AbstractCachesTest { + @Test + public void should_stop_and_clean_temp_dir() { + File tempDir = cachesManager.tempDir(); + assertThat(tempDir).isDirectory().exists(); + assertThat(cachesManager.persistit()).isNotNull(); + assertThat(cachesManager.persistit().isInitialized()).isTrue(); + + cachesManager.stop(); + + assertThat(tempDir).doesNotExist(); + assertThat(cachesManager.tempDir()).isNull(); + assertThat(cachesManager.persistit()).isNull(); + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/index/CachesTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/index/CachesTest.java new file mode 100644 index 00000000000..2a01ac38025 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/index/CachesTest.java @@ -0,0 +1,89 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.index; + +import java.io.Serializable; + +import com.persistit.exception.PersistitException; +import org.junit.Test; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.fail; + +public class CachesTest extends AbstractCachesTest { + @Test + public void should_create_cache() { + Cache<Element> cache = caches.createCache("foo"); + assertThat(cache).isNotNull(); + } + + @Test + public void should_not_create_cache_twice() { + caches.<Element>createCache("foo"); + try { + caches.<Element>createCache("foo"); + fail(); + } catch (IllegalStateException e) { + // ok + } + } + + @Test + public void should_clean_resources() { + Cache<String> c = caches.<String>createCache("test1"); + for (int i = 0; i < 1_000_000; i++) { + c.put("a" + i, "a" + i); + } + + caches.stop(); + + // manager continues up + assertThat(cachesManager.persistit().isInitialized()).isTrue(); + + caches = new Caches(cachesManager); + caches.start(); + caches.createCache("test1"); + } + + @Test + public void leak_test() throws PersistitException { + caches.stop(); + + int len = 1 * 1024 * 1024; + StringBuilder sb = new StringBuilder(len); + for (int i = 0; i < len; i++) { + sb.append("a"); + } + + for (int i = 0; i < 3; i++) { + caches = new Caches(cachesManager); + caches.start(); + Cache<String> c = caches.<String>createCache("test" + i); + c.put("key" + i, sb.toString()); + cachesManager.persistit().flush(); + + caches.stop(); + } + } + + private static class Element implements Serializable { + private static final long serialVersionUID = 1L; + + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/index/DefaultIndexTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/index/DefaultIndexTest.java new file mode 100644 index 00000000000..8dae9e19ad0 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/index/DefaultIndexTest.java @@ -0,0 +1,163 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.index; + +import java.io.IOException; +import org.apache.commons.io.FileUtils; +import org.junit.Before; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.sonar.api.batch.bootstrap.ProjectDefinition; +import org.sonar.api.measures.Measure; +import org.sonar.api.measures.MeasuresFilters; +import org.sonar.api.profiles.RulesProfile; +import org.sonar.api.resources.Directory; +import org.sonar.api.resources.File; +import org.sonar.api.resources.Java; +import org.sonar.api.resources.Project; +import org.sonar.api.resources.Resource; +import org.sonar.api.rules.Rule; +import org.sonar.api.rules.RuleFinder; +import org.sonar.api.scan.filesystem.PathResolver; +import org.sonar.batch.DefaultProjectTree; +import org.sonar.batch.scan.measure.MeasureCache; +import org.sonar.batch.sensor.DefaultSensorStorage; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class DefaultIndexTest { + + @org.junit.Rule + public TemporaryFolder temp = new TemporaryFolder(); + + DefaultIndex index = null; + Rule rule; + RuleFinder ruleFinder; + Project project; + Project moduleA; + Project moduleB; + Project moduleB1; + + private java.io.File baseDir; + + @Before + public void createIndex() throws IOException { + ruleFinder = mock(RuleFinder.class); + + DefaultProjectTree projectTree = mock(DefaultProjectTree.class); + BatchComponentCache resourceCache = new BatchComponentCache(); + index = new DefaultIndex(resourceCache, projectTree, mock(MeasureCache.class), new PathResolver()); + + baseDir = temp.newFolder(); + project = new Project("project"); + when(projectTree.getProjectDefinition(project)).thenReturn(ProjectDefinition.create().setBaseDir(baseDir)); + moduleA = new Project("moduleA").setParent(project); + when(projectTree.getProjectDefinition(moduleA)).thenReturn(ProjectDefinition.create().setBaseDir(new java.io.File(baseDir, "moduleA"))); + moduleB = new Project("moduleB").setParent(project); + when(projectTree.getProjectDefinition(moduleB)).thenReturn(ProjectDefinition.create().setBaseDir(new java.io.File(baseDir, "moduleB"))); + moduleB1 = new Project("moduleB1").setParent(moduleB); + when(projectTree.getProjectDefinition(moduleB1)).thenReturn(ProjectDefinition.create().setBaseDir(new java.io.File(baseDir, "moduleB/moduleB1"))); + + RulesProfile rulesProfile = RulesProfile.create(); + rule = Rule.create("repoKey", "ruleKey", "Rule"); + rule.setId(1); + rulesProfile.activateRule(rule, null); + index.setCurrentProject(project, mock(DefaultSensorStorage.class)); + index.doStart(project); + } + + @Test + public void shouldIndexParentOfDeprecatedFiles() { + File file = File.create("src/org/foo/Bar.java", null, false); + assertThat(index.index(file)).isTrue(); + + Directory reference = Directory.create("src/org/foo"); + assertThat(index.getResource(reference).getName()).isEqualTo("src/org/foo"); + assertThat(index.isIndexed(reference, true)).isTrue(); + assertThat(index.isExcluded(reference)).isFalse(); + assertThat(index.getChildren(reference)).hasSize(1); + assertThat(index.getParent(reference)).isInstanceOf(Project.class); + } + + @Test + public void shouldIndexTreeOfResources() { + Directory directory = Directory.create("src/org/foo"); + File file = File.create("src/org/foo/Bar.java", Java.INSTANCE, false); + + assertThat(index.index(directory)).isTrue(); + assertThat(index.index(file, directory)).isTrue(); + + File fileRef = File.create("src/org/foo/Bar.java", null, false); + assertThat(index.getResource(fileRef).getKey()).isEqualTo("src/org/foo/Bar.java"); + assertThat(index.getResource(fileRef).getLanguage().getKey()).isEqualTo("java"); + assertThat(index.isIndexed(fileRef, true)).isTrue(); + assertThat(index.isExcluded(fileRef)).isFalse(); + assertThat(index.getChildren(fileRef)).isEmpty(); + assertThat(index.getParent(fileRef)).isInstanceOf(Directory.class); + } + + @Test + public void shouldGetSource() throws Exception { + Directory directory = Directory.create("src/org/foo"); + File file = File.create("src/org/foo/Bar.java", Java.INSTANCE, false); + FileUtils.write(new java.io.File(baseDir, "src/org/foo/Bar.java"), "Foo bar"); + + assertThat(index.index(directory)).isTrue(); + assertThat(index.index(file, directory)).isTrue(); + + File fileRef = File.create("src/org/foo/Bar.java", null, false); + assertThat(index.getSource(fileRef)).isEqualTo("Foo bar"); + } + + @Test + public void shouldNotIndexResourceIfParentNotIndexed() { + Directory directory = Directory.create("src/org/other"); + File file = File.create("src/org/foo/Bar.java", null, false); + + assertThat(index.index(file, directory)).isFalse(); + + File fileRef = File.create("src/org/foo/Bar.java", null, false); + assertThat(index.isIndexed(directory, true)).isFalse(); + assertThat(index.isIndexed(fileRef, true)).isFalse(); + assertThat(index.isExcluded(fileRef)).isFalse(); + assertThat(index.getChildren(fileRef)).isEmpty(); + assertThat(index.getParent(fileRef)).isNull(); + } + + @Test + public void shouldNotIndexResourceWhenAddingMeasure() { + Resource dir = Directory.create("src/org/foo"); + index.addMeasure(dir, new Measure("ncloc").setValue(50.0)); + + assertThat(index.isIndexed(dir, true)).isFalse(); + assertThat(index.getMeasures(dir, MeasuresFilters.metric("ncloc"))).isNull(); + } + + @Test + public void shouldComputePathOfIndexedModules() { + assertThat(index.getResource(project).getPath()).isNull(); + assertThat(index.getResource(moduleA).getPath()).isEqualTo("moduleA"); + assertThat(index.getResource(moduleB).getPath()).isEqualTo("moduleB"); + assertThat(index.getResource(moduleB1).getPath()).isEqualTo("moduleB1"); + } + +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/DefaultFilterableIssueTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/DefaultFilterableIssueTest.java new file mode 100644 index 00000000000..daded262498 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/DefaultFilterableIssueTest.java @@ -0,0 +1,86 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.issue; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.when; + +import java.util.Date; + +import static org.mockito.Mockito.mock; + +import org.junit.Before; +import org.junit.Test; +import org.sonar.api.resources.Project; +import org.sonar.scanner.protocol.Constants.Severity; +import org.sonar.scanner.protocol.output.ScannerReport.Issue; + +public class DefaultFilterableIssueTest { + private DefaultFilterableIssue issue; + private Project mockedProject; + private String componentKey; + private Issue rawIssue; + + @Before + public void setUp() { + mockedProject = mock(Project.class); + componentKey = "component"; + } + + private Issue createIssue() { + Issue.Builder builder = Issue.newBuilder(); + + builder.setGap(3.0); + builder.setLine(30); + builder.setSeverity(Severity.MAJOR); + return builder.build(); + } + + private Issue createIssueWithoutFields() { + Issue.Builder builder = Issue.newBuilder(); + builder.setSeverity(Severity.MAJOR); + return builder.build(); + } + + @Test + public void testRoundTrip() { + rawIssue = createIssue(); + issue = new DefaultFilterableIssue(mockedProject, rawIssue, componentKey); + + when(mockedProject.getAnalysisDate()).thenReturn(new Date(10_000)); + when(mockedProject.getEffectiveKey()).thenReturn("projectKey"); + + assertThat(issue.componentKey()).isEqualTo(componentKey); + assertThat(issue.creationDate()).isEqualTo(new Date(10_000)); + assertThat(issue.line()).isEqualTo(30); + assertThat(issue.projectKey()).isEqualTo("projectKey"); + assertThat(issue.effortToFix()).isEqualTo(3.0); + assertThat(issue.severity()).isEqualTo("MAJOR"); + } + + @Test + public void nullValues() { + rawIssue = createIssueWithoutFields(); + issue = new DefaultFilterableIssue(mockedProject, rawIssue, componentKey); + + assertThat(issue.line()).isNull(); + assertThat(issue.effortToFix()).isNull(); + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/DefaultIssueCallbackTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/DefaultIssueCallbackTest.java new file mode 100644 index 00000000000..7be36bd3bc5 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/DefaultIssueCallbackTest.java @@ -0,0 +1,137 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.issue; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import org.sonar.batch.issue.tracking.TrackedIssue; + +import org.sonar.api.batch.rule.Rule; +import org.sonar.api.rule.RuleKey; +import org.sonar.batch.bootstrapper.IssueListener.Issue; +import org.mockito.MockitoAnnotations; +import org.mockito.Mock; +import org.sonar.api.batch.rule.Rules; +import org.sonar.batch.repository.user.UserRepositoryLoader; +import org.sonar.scanner.protocol.input.ScannerInput; +import org.sonar.batch.bootstrapper.IssueListener; +import org.junit.Before; +import com.google.common.collect.ImmutableList; + +import java.util.LinkedList; +import java.util.List; + +import static org.mockito.Matchers.any; +import static org.assertj.core.api.Assertions.assertThat; +import org.junit.Test; + +public class DefaultIssueCallbackTest { + @Mock + private IssueCache issueCache; + @Mock + private UserRepositoryLoader userRepository; + @Mock + private Rules rules; + + private TrackedIssue issue; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + RuleKey ruleKey = RuleKey.of("repo", "key"); + issue = new TrackedIssue(); + issue.setKey("key"); + issue.setAssignee("user"); + issue.setRuleKey(ruleKey); + + when(issueCache.all()).thenReturn(ImmutableList.of(issue)); + + ScannerInput.User.Builder userBuilder = ScannerInput.User.newBuilder(); + userBuilder.setLogin("user"); + userBuilder.setName("name"); + when(userRepository.load("user")).thenReturn(userBuilder.build()); + + Rule r = mock(Rule.class); + when(r.name()).thenReturn("rule name"); + when(rules.find(ruleKey)).thenReturn(r); + } + + @Test + public void testWithoutListener() { + DefaultIssueCallback issueCallback = new DefaultIssueCallback(issueCache, userRepository, rules); + issueCallback.execute(); + } + + @Test + public void testWithListener() { + final List<IssueListener.Issue> issueList = new LinkedList<>(); + IssueListener listener = new IssueListener() { + @Override + public void handle(Issue issue) { + issueList.add(issue); + } + }; + + DefaultIssueCallback issueCallback = new DefaultIssueCallback(issueCache, listener, userRepository, rules); + issueCallback.execute(); + + assertThat(issueList).hasSize(1); + Issue callbackIssue = issueList.get(0); + + assertThat(callbackIssue.getAssigneeName()).isEqualTo("name"); + assertThat(callbackIssue.getRuleName()).isEqualTo("rule name"); + } + + @Test + public void testWithNulls() { + final List<IssueListener.Issue> issueList = new LinkedList<>(); + IssueListener listener = new IssueListener() { + @Override + public void handle(Issue issue) { + issueList.add(issue); + } + }; + + issue.setKey(null); + issue.setAssignee(null); + + DefaultIssueCallback issueCallback = new DefaultIssueCallback(issueCache, listener, userRepository, rules); + issueCallback.execute(); + } + + @Test + public void testDecorationNotFound() { + final List<IssueListener.Issue> issueList = new LinkedList<>(); + IssueListener listener = new IssueListener() { + @Override + public void handle(Issue issue) { + issueList.add(issue); + } + }; + + when(userRepository.load(any(String.class))).thenReturn(null); + when(rules.find(any(RuleKey.class))).thenReturn(null); + + DefaultIssueCallback issueCallback = new DefaultIssueCallback(issueCache, listener, userRepository, rules); + issueCallback.execute(); + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/DefaultIssueFilterChainTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/DefaultIssueFilterChainTest.java new file mode 100644 index 00000000000..041bc8493a2 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/DefaultIssueFilterChainTest.java @@ -0,0 +1,95 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.issue; + +import org.sonar.api.scan.issue.filter.FilterableIssue; + +import org.junit.Test; +import org.sonar.api.scan.issue.filter.IssueFilter; +import org.sonar.api.scan.issue.filter.IssueFilterChain; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.fail; +import static org.mockito.Mockito.mock; + +public class DefaultIssueFilterChainTest { + private final FilterableIssue issue = mock(FilterableIssue.class); + + @Test + public void should_accept_when_no_filter() { + assertThat(new DefaultIssueFilterChain().accept(issue)).isTrue(); + } + + class PassingFilter implements IssueFilter { + @Override + public boolean accept(FilterableIssue issue, IssueFilterChain chain) { + return chain.accept(issue); + } + } + + class AcceptingFilter implements IssueFilter { + @Override + public boolean accept(FilterableIssue issue, IssueFilterChain chain) { + return true; + } + } + + class RefusingFilter implements IssueFilter { + @Override + public boolean accept(FilterableIssue issue, IssueFilterChain chain) { + return false; + } + } + + class FailingFilter implements IssueFilter { + @Override + public boolean accept(FilterableIssue issue, IssueFilterChain chain) { + fail(); + return false; + } + + } + + @Test + public void should_accept_if_all_filters_pass() { + assertThat(new DefaultIssueFilterChain( + new PassingFilter(), + new PassingFilter(), + new PassingFilter() + ).accept(issue)).isTrue(); + } + + @Test + public void should_accept_and_not_go_further_if_filter_accepts() { + assertThat(new DefaultIssueFilterChain( + new PassingFilter(), + new AcceptingFilter(), + new FailingFilter() + ).accept(issue)).isTrue(); + } + + @Test + public void should_refuse_and_not_go_further_if_filter_refuses() { + assertThat(new DefaultIssueFilterChain( + new PassingFilter(), + new RefusingFilter(), + new FailingFilter() + ).accept(issue)).isFalse(); + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/DefaultProjectIssuesTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/DefaultProjectIssuesTest.java new file mode 100644 index 00000000000..9448206b1d6 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/DefaultProjectIssuesTest.java @@ -0,0 +1,78 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.issue; + +import org.sonar.batch.issue.tracking.TrackedIssue; + +import com.google.common.collect.Lists; +import org.junit.Test; +import org.sonar.api.issue.Issue; +import org.sonar.core.issue.DefaultIssue; +import org.sonar.api.rule.RuleKey; +import org.sonar.api.rule.Severity; + +import java.util.Arrays; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class DefaultProjectIssuesTest { + + static final RuleKey SQUID_RULE_KEY = RuleKey.of("squid", "AvoidCycle"); + + IssueCache cache = mock(IssueCache.class); + DefaultProjectIssues projectIssues = new DefaultProjectIssues(cache); + + @Test + public void should_get_all_issues() { + DefaultIssue issueOnModule = new DefaultIssue().setKey("1").setRuleKey(SQUID_RULE_KEY).setComponentKey("org.apache:struts-core"); + DefaultIssue issueInModule = new DefaultIssue().setKey("2").setRuleKey(SQUID_RULE_KEY).setComponentKey("org.apache:struts-core:Action"); + DefaultIssue resolvedIssueInModule = new DefaultIssue().setKey("3").setRuleKey(SQUID_RULE_KEY).setComponentKey("org.apache:struts-core:Action") + .setResolution(Issue.RESOLUTION_FIXED); + + DefaultIssue issueOnRoot = new DefaultIssue().setKey("4").setRuleKey(SQUID_RULE_KEY).setSeverity(Severity.CRITICAL).setComponentKey("org.apache:struts"); + DefaultIssue issueInRoot = new DefaultIssue().setKey("5").setRuleKey(SQUID_RULE_KEY).setSeverity(Severity.CRITICAL).setComponentKey("org.apache:struts:FileInRoot"); + when(cache.all()).thenReturn(Arrays.<TrackedIssue>asList( + toTrackedIssue(issueOnRoot), toTrackedIssue(issueInRoot), + toTrackedIssue(issueOnModule), toTrackedIssue(issueInModule), toTrackedIssue(resolvedIssueInModule) + )); + + // unresolved issues + List<Issue> issues = Lists.newArrayList(projectIssues.issues()); + assertThat(issues).containsOnly(issueOnRoot, issueInRoot, issueInModule, issueOnModule); + + List<Issue> resolvedIssues = Lists.newArrayList(projectIssues.resolvedIssues()); + assertThat(resolvedIssues).containsOnly(resolvedIssueInModule); + } + + private TrackedIssue toTrackedIssue(DefaultIssue issue) { + TrackedIssue trackedIssue = new TrackedIssue(); + + trackedIssue.setKey(issue.key()); + trackedIssue.setRuleKey(issue.ruleKey()); + trackedIssue.setComponentKey(issue.componentKey()); + trackedIssue.setSeverity(issue.severity()); + trackedIssue.setResolution(issue.resolution()); + + return trackedIssue; + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/DeprecatedIssueAdapterForFilterTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/DeprecatedIssueAdapterForFilterTest.java new file mode 100644 index 00000000000..b4818adae0e --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/DeprecatedIssueAdapterForFilterTest.java @@ -0,0 +1,157 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.issue; + +import java.util.Date; +import org.junit.Test; +import org.sonar.api.issue.Issue; +import org.sonar.api.resources.Project; +import org.sonar.api.rule.RuleKey; +import org.sonar.scanner.protocol.Constants.Severity; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.fail; + +public class DeprecatedIssueAdapterForFilterTest { + + private static final String PROJECT_KEY = "foo"; + private static final Date ANALYSIS_DATE = new Date(); + private static final String COMPONENT_KEY = "foo:src/Foo.java"; + + @Test + public void improve_coverage() { + DeprecatedIssueAdapterForFilter issue = new DeprecatedIssueAdapterForFilter(new Project(PROJECT_KEY).setAnalysisDate(ANALYSIS_DATE), + org.sonar.scanner.protocol.output.ScannerReport.Issue.newBuilder() + .setRuleRepository("repo") + .setRuleKey("key") + .setSeverity(Severity.BLOCKER) + .setMsg("msg") + .build(), + COMPONENT_KEY); + DeprecatedIssueAdapterForFilter issue2 = new DeprecatedIssueAdapterForFilter(new Project(PROJECT_KEY).setAnalysisDate(ANALYSIS_DATE), + org.sonar.scanner.protocol.output.ScannerReport.Issue.newBuilder() + .setRuleRepository("repo") + .setRuleKey("key") + .setSeverity(Severity.BLOCKER) + .setMsg("msg") + .setLine(1) + .setGap(2.0) + .build(), + COMPONENT_KEY); + + try { + issue.key(); + fail("Should be unsupported"); + } catch (Exception e) { + assertThat(e).isExactlyInstanceOf(UnsupportedOperationException.class).hasMessage("Not available for issues filters"); + } + + assertThat(issue.componentKey()).isEqualTo(COMPONENT_KEY); + assertThat(issue.ruleKey()).isEqualTo(RuleKey.of("repo", "key")); + + try { + issue.language(); + fail("Should be unsupported"); + } catch (Exception e) { + assertThat(e).isExactlyInstanceOf(UnsupportedOperationException.class).hasMessage("Not available for issues filters"); + } + + assertThat(issue.severity()).isEqualTo("BLOCKER"); + assertThat(issue.message()).isEqualTo("msg"); + assertThat(issue.line()).isNull(); + assertThat(issue2.line()).isEqualTo(1); + assertThat(issue.effortToFix()).isNull(); + assertThat(issue2.effortToFix()).isEqualTo(2.0); + assertThat(issue.status()).isEqualTo(Issue.STATUS_OPEN); + assertThat(issue.resolution()).isNull(); + + try { + issue.reporter(); + fail("Should be unsupported"); + } catch (Exception e) { + assertThat(e).isExactlyInstanceOf(UnsupportedOperationException.class).hasMessage("Not available for issues filters"); + } + + assertThat(issue.assignee()).isNull(); + assertThat(issue.creationDate()).isEqualTo(ANALYSIS_DATE); + assertThat(issue.updateDate()).isNull(); + assertThat(issue.closeDate()).isNull(); + assertThat(issue.attribute(PROJECT_KEY)).isNull(); + + try { + issue.authorLogin(); + fail("Should be unsupported"); + } catch (Exception e) { + assertThat(e).isExactlyInstanceOf(UnsupportedOperationException.class).hasMessage("Not available for issues filters"); + } + + try { + issue.actionPlanKey(); + fail("Should be unsupported"); + } catch (Exception e) { + assertThat(e).isExactlyInstanceOf(UnsupportedOperationException.class).hasMessage("Not available for issues filters"); + } + + try { + issue.comments(); + fail("Should be unsupported"); + } catch (Exception e) { + assertThat(e).isExactlyInstanceOf(UnsupportedOperationException.class).hasMessage("Not available for issues filters"); + } + + try { + issue.isNew(); + fail("Should be unsupported"); + } catch (Exception e) { + assertThat(e).isExactlyInstanceOf(UnsupportedOperationException.class).hasMessage("Not available for issues filters"); + } + + try { + issue.debt(); + fail("Should be unsupported"); + } catch (Exception e) { + assertThat(e).isExactlyInstanceOf(UnsupportedOperationException.class).hasMessage("Not available for issues filters"); + } + + assertThat(issue.projectKey()).isEqualTo(PROJECT_KEY); + + try { + issue.projectUuid(); + fail("Should be unsupported"); + } catch (Exception e) { + assertThat(e).isExactlyInstanceOf(UnsupportedOperationException.class).hasMessage("Not available for issues filters"); + } + + try { + issue.componentUuid(); + fail("Should be unsupported"); + } catch (Exception e) { + assertThat(e).isExactlyInstanceOf(UnsupportedOperationException.class).hasMessage("Not available for issues filters"); + } + + try { + issue.tags(); + fail("Should be unsupported"); + } catch (Exception e) { + assertThat(e).isExactlyInstanceOf(UnsupportedOperationException.class).hasMessage("Not available for issues filters"); + } + } + +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/DeprecatedIssueFilterChainTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/DeprecatedIssueFilterChainTest.java new file mode 100644 index 00000000000..c80b1d64a82 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/DeprecatedIssueFilterChainTest.java @@ -0,0 +1,96 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.issue; + +import org.junit.Test; +import org.sonar.api.issue.Issue; +import org.sonar.api.issue.batch.IssueFilter; +import org.sonar.api.issue.batch.IssueFilterChain; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.fail; +import static org.mockito.Mockito.mock; + +public class DeprecatedIssueFilterChainTest { + + private final Issue issue = mock(Issue.class); + + @Test + public void should_accept_when_no_filter() { + assertThat(new DeprecatedIssueFilterChain().accept(issue)).isTrue(); + } + + class PassingFilter implements IssueFilter { + @Override + public boolean accept(Issue issue, IssueFilterChain chain) { + return chain.accept(issue); + } + } + + class AcceptingFilter implements IssueFilter { + @Override + public boolean accept(Issue issue, IssueFilterChain chain) { + return true; + } + } + + class RefusingFilter implements IssueFilter { + @Override + public boolean accept(Issue issue, IssueFilterChain chain) { + return false; + } + } + + class FailingFilter implements IssueFilter { + @Override + public boolean accept(Issue issue, IssueFilterChain chain) { + fail(); + return false; + } + + } + + @Test + public void should_accept_if_all_filters_pass() { + assertThat(new DeprecatedIssueFilterChain( + new PassingFilter(), + new PassingFilter(), + new PassingFilter() + ).accept(issue)).isTrue(); + } + + @Test + public void should_accept_and_not_go_further_if_filter_accepts() { + assertThat(new DeprecatedIssueFilterChain( + new PassingFilter(), + new AcceptingFilter(), + new FailingFilter() + ).accept(issue)).isTrue(); + } + + @Test + public void should_refuse_and_not_go_further_if_filter_refuses() { + assertThat(new DeprecatedIssueFilterChain( + new PassingFilter(), + new RefusingFilter(), + new FailingFilter() + ).accept(issue)).isFalse(); + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/IssuableFactoryTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/IssuableFactoryTest.java new file mode 100644 index 00000000000..65856e1e808 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/IssuableFactoryTest.java @@ -0,0 +1,57 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.issue; + +import org.junit.Test; +import org.sonar.api.issue.Issuable; +import org.sonar.api.resources.File; +import org.sonar.api.resources.Project; +import org.sonar.batch.DefaultProjectTree; +import org.sonar.batch.index.BatchComponent; +import org.sonar.batch.sensor.DefaultSensorContext; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + +public class IssuableFactoryTest { + + ModuleIssues moduleIssues = mock(ModuleIssues.class); + DefaultProjectTree projectTree = mock(DefaultProjectTree.class); + + @Test + public void file_should_be_issuable() { + IssuableFactory factory = new IssuableFactory(mock(DefaultSensorContext.class)); + BatchComponent component = new BatchComponent(1, File.create("foo/bar.c").setEffectiveKey("foo/bar.c"), null); + Issuable issuable = factory.loadPerspective(Issuable.class, component); + + assertThat(issuable).isNotNull(); + assertThat(issuable.issues()).isEmpty(); + } + + @Test + public void project_should_be_issuable() { + IssuableFactory factory = new IssuableFactory(mock(DefaultSensorContext.class)); + BatchComponent component = new BatchComponent(1, new Project("Foo").setEffectiveKey("foo"), null); + Issuable issuable = factory.loadPerspective(Issuable.class, component); + + assertThat(issuable).isNotNull(); + assertThat(issuable.issues()).isEmpty(); + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/IssueCacheTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/IssueCacheTest.java new file mode 100644 index 00000000000..6d9f42a8ede --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/IssueCacheTest.java @@ -0,0 +1,98 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.issue; + +import org.sonar.batch.issue.tracking.TrackedIssue; + +import org.sonar.batch.index.AbstractCachesTest; +import com.google.common.base.Function; +import com.google.common.collect.Collections2; +import com.google.common.collect.ImmutableList; +import org.junit.Test; +import org.sonar.api.rule.Severity; + +import javax.annotation.Nullable; + +import java.util.Collection; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +public class IssueCacheTest extends AbstractCachesTest { + + @Test + public void should_add_new_issue() { + IssueCache cache = new IssueCache(caches); + TrackedIssue issue1 = createIssue("111", "org.struts.Action", null); + TrackedIssue issue2 = createIssue("222", "org.struts.Action", null); + TrackedIssue issue3 = createIssue("333", "org.struts.Filter", null); + issue3.setAssignee("foo"); + cache.put(issue1).put(issue2).put(issue3); + + assertThat(issueKeys(cache.byComponent("org.struts.Action"))).containsOnly("111", "222"); + assertThat(issueKeys(cache.byComponent("org.struts.Filter"))).containsOnly("333"); + assertThat(cache.byComponent("org.struts.Filter").iterator().next().assignee()).isEqualTo("foo"); + } + + @Test + public void should_update_existing_issue() { + IssueCache cache = new IssueCache(caches); + TrackedIssue issue = createIssue("111", "org.struts.Action", Severity.BLOCKER); + cache.put(issue); + + issue.setSeverity(Severity.MINOR); + cache.put(issue); + + List<TrackedIssue> issues = ImmutableList.copyOf(cache.byComponent("org.struts.Action")); + assertThat(issues).hasSize(1); + TrackedIssue reloaded = issues.iterator().next(); + assertThat(reloaded.key()).isEqualTo("111"); + assertThat(reloaded.severity()).isEqualTo(Severity.MINOR); + } + + @Test + public void should_get_all_issues() { + IssueCache cache = new IssueCache(caches); + TrackedIssue issue1 = createIssue("111", "org.struts.Action", Severity.BLOCKER); + TrackedIssue issue2 = createIssue("222", "org.struts.Filter", Severity.INFO); + cache.put(issue1).put(issue2); + + List<TrackedIssue> issues = ImmutableList.copyOf(cache.all()); + assertThat(issues).containsOnly(issue1, issue2); + } + + private Collection<String> issueKeys(Iterable<TrackedIssue> issues) { + return Collections2.transform(ImmutableList.copyOf(issues), new Function<TrackedIssue, String>() { + @Override + public String apply(@Nullable TrackedIssue issue) { + return issue.key(); + } + }); + } + + private TrackedIssue createIssue(String key, String componentKey, String severity) { + TrackedIssue issue = new TrackedIssue(); + issue.setKey(key); + issue.setComponentKey(componentKey); + issue.setSeverity(severity); + + return issue; + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/ModuleIssuesTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/ModuleIssuesTest.java new file mode 100644 index 00000000000..af84563a8b8 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/ModuleIssuesTest.java @@ -0,0 +1,221 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.issue; + +import java.io.StringReader; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.fs.internal.FileMetadata; +import org.sonar.api.batch.rule.internal.ActiveRulesBuilder; +import org.sonar.api.batch.rule.internal.RulesBuilder; +import org.sonar.api.batch.sensor.issue.internal.DefaultIssue; +import org.sonar.api.batch.sensor.issue.internal.DefaultIssueLocation; +import org.sonar.api.resources.File; +import org.sonar.api.rule.RuleKey; +import org.sonar.api.rule.Severity; +import org.sonar.api.utils.MessageException; +import org.sonar.batch.index.BatchComponentCache; +import org.sonar.batch.report.ReportPublisher; +import org.sonar.scanner.protocol.output.ScannerReport; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.fail; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.RETURNS_DEEP_STUBS; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class ModuleIssuesTest { + + static final RuleKey SQUID_RULE_KEY = RuleKey.of("squid", "AvoidCycle"); + static final String SQUID_RULE_NAME = "Avoid Cycle"; + + @Mock + IssueFilters filters; + + ActiveRulesBuilder activeRulesBuilder = new ActiveRulesBuilder(); + RulesBuilder ruleBuilder = new RulesBuilder(); + + ModuleIssues moduleIssues; + + BatchComponentCache componentCache = new BatchComponentCache(); + InputFile file = new DefaultInputFile("foo", "src/Foo.php").initMetadata(new FileMetadata().readMetadata(new StringReader("Foo\nBar\nBiz\n"))); + ReportPublisher reportPublisher = mock(ReportPublisher.class, RETURNS_DEEP_STUBS); + + @Before + public void prepare() { + componentCache.add(File.create("src/Foo.php").setEffectiveKey("foo:src/Foo.php"), null).setInputComponent(file); + } + + @Test + public void fail_on_unknown_rule() { + initModuleIssues(); + DefaultIssue issue = new DefaultIssue() + .at(new DefaultIssueLocation().on(file).at(file.selectLine(3)).message("Foo")) + .forRule(SQUID_RULE_KEY); + try { + moduleIssues.initAndAddIssue(issue); + fail(); + } catch (Exception e) { + assertThat(e).isInstanceOf(MessageException.class); + } + + verifyZeroInteractions(reportPublisher); + } + + @Test + public void fail_if_rule_has_no_name_and_issue_has_no_message() { + ruleBuilder.add(SQUID_RULE_KEY).setInternalKey(SQUID_RULE_KEY.rule()); + initModuleIssues(); + DefaultIssue issue = new DefaultIssue() + .at(new DefaultIssueLocation().on(file).at(file.selectLine(3)).message("")) + .forRule(SQUID_RULE_KEY); + try { + moduleIssues.initAndAddIssue(issue); + fail(); + } catch (Exception e) { + assertThat(e).isInstanceOf(MessageException.class); + } + + verifyZeroInteractions(reportPublisher); + } + + @Test + public void ignore_null_active_rule() { + ruleBuilder.add(SQUID_RULE_KEY).setName(SQUID_RULE_NAME); + initModuleIssues(); + DefaultIssue issue = new DefaultIssue() + .at(new DefaultIssueLocation().on(file).at(file.selectLine(3)).message("Foo")) + .forRule(SQUID_RULE_KEY); + boolean added = moduleIssues.initAndAddIssue(issue); + + assertThat(added).isFalse(); + verifyZeroInteractions(reportPublisher); + } + + @Test + public void ignore_null_rule_of_active_rule() { + ruleBuilder.add(SQUID_RULE_KEY).setName(SQUID_RULE_NAME); + activeRulesBuilder.create(SQUID_RULE_KEY).activate(); + initModuleIssues(); + + DefaultIssue issue = new DefaultIssue() + .at(new DefaultIssueLocation().on(file).at(file.selectLine(3)).message("Foo")) + .forRule(SQUID_RULE_KEY); + boolean added = moduleIssues.initAndAddIssue(issue); + + assertThat(added).isFalse(); + verifyZeroInteractions(reportPublisher); + } + + @Test + public void add_issue_to_cache() { + ruleBuilder.add(SQUID_RULE_KEY).setName(SQUID_RULE_NAME); + activeRulesBuilder.create(SQUID_RULE_KEY).setSeverity(Severity.INFO).activate(); + initModuleIssues(); + + DefaultIssue issue = new DefaultIssue() + .at(new DefaultIssueLocation().on(file).at(file.selectLine(3)).message("Foo")) + .forRule(SQUID_RULE_KEY) + .overrideSeverity(org.sonar.api.batch.rule.Severity.CRITICAL); + + when(filters.accept(anyString(), any(ScannerReport.Issue.class))).thenReturn(true); + + boolean added = moduleIssues.initAndAddIssue(issue); + + assertThat(added).isTrue(); + ArgumentCaptor<ScannerReport.Issue> argument = ArgumentCaptor.forClass(ScannerReport.Issue.class); + verify(reportPublisher.getWriter()).appendComponentIssue(eq(1), argument.capture()); + assertThat(argument.getValue().getSeverity()).isEqualTo(org.sonar.scanner.protocol.Constants.Severity.CRITICAL); + } + + @Test + public void use_severity_from_active_rule_if_no_severity_on_issue() { + ruleBuilder.add(SQUID_RULE_KEY).setName(SQUID_RULE_NAME); + activeRulesBuilder.create(SQUID_RULE_KEY).setSeverity(Severity.INFO).activate(); + initModuleIssues(); + + DefaultIssue issue = new DefaultIssue() + .at(new DefaultIssueLocation().on(file).at(file.selectLine(3)).message("Foo")) + .forRule(SQUID_RULE_KEY); + when(filters.accept(anyString(), any(ScannerReport.Issue.class))).thenReturn(true); + moduleIssues.initAndAddIssue(issue); + + ArgumentCaptor<ScannerReport.Issue> argument = ArgumentCaptor.forClass(ScannerReport.Issue.class); + verify(reportPublisher.getWriter()).appendComponentIssue(eq(1), argument.capture()); + assertThat(argument.getValue().getSeverity()).isEqualTo(org.sonar.scanner.protocol.Constants.Severity.INFO); + } + + @Test + public void use_rule_name_if_no_message() { + ruleBuilder.add(SQUID_RULE_KEY).setName(SQUID_RULE_NAME); + activeRulesBuilder.create(SQUID_RULE_KEY).setSeverity(Severity.INFO).setName(SQUID_RULE_NAME).activate(); + initModuleIssues(); + + DefaultIssue issue = new DefaultIssue() + .at(new DefaultIssueLocation().on(file).at(file.selectLine(3)).message("")) + .forRule(SQUID_RULE_KEY); + when(filters.accept(anyString(), any(ScannerReport.Issue.class))).thenReturn(true); + + boolean added = moduleIssues.initAndAddIssue(issue); + + assertThat(added).isTrue(); + ArgumentCaptor<ScannerReport.Issue> argument = ArgumentCaptor.forClass(ScannerReport.Issue.class); + verify(reportPublisher.getWriter()).appendComponentIssue(eq(1), argument.capture()); + assertThat(argument.getValue().getMsg()).isEqualTo("Avoid Cycle"); + } + + @Test + public void filter_issue() { + ruleBuilder.add(SQUID_RULE_KEY).setName(SQUID_RULE_NAME); + activeRulesBuilder.create(SQUID_RULE_KEY).setSeverity(Severity.INFO).activate(); + initModuleIssues(); + + DefaultIssue issue = new DefaultIssue() + .at(new DefaultIssueLocation().on(file).at(file.selectLine(3)).message("")) + .forRule(SQUID_RULE_KEY); + + when(filters.accept(anyString(), any(ScannerReport.Issue.class))).thenReturn(false); + + boolean added = moduleIssues.initAndAddIssue(issue); + + assertThat(added).isFalse(); + verifyZeroInteractions(reportPublisher); + } + + /** + * Every rules and active rules has to be added in builders before creating ModuleIssues + */ + private void initModuleIssues() { + moduleIssues = new ModuleIssues(activeRulesBuilder.build(), ruleBuilder.build(), filters, reportPublisher, componentCache); + } + +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/TrackedIssueAdapterTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/TrackedIssueAdapterTest.java new file mode 100644 index 00000000000..11fc560b318 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/TrackedIssueAdapterTest.java @@ -0,0 +1,84 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.issue; + +import java.util.Date; +import org.junit.Test; +import org.sonar.api.issue.Issue; +import org.sonar.api.rule.RuleKey; +import org.sonar.batch.issue.tracking.TrackedIssue; + +import static org.assertj.core.api.Assertions.assertThat; + +public class TrackedIssueAdapterTest { + + @Test + public void improve_coverage() { + Date creationDate = new Date(); + TrackedIssue trackedIssue = new TrackedIssue() + .setKey("XYZ123") + .setComponentKey("foo") + .setRuleKey(RuleKey.of("repo", "rule")) + .setSeverity("MAJOR") + .setMessage("msg") + .setStartLine(1) + .setGap(2.0) + .setStatus("RESOLVED") + .setResolution("FIXED") + .setReporter("toto") + .setAssignee("tata") + .setNew(true) + .setCreationDate(creationDate); + Issue issue = new TrackedIssueAdapter(trackedIssue); + assertThat(issue.key()).isEqualTo("XYZ123"); + assertThat(issue.componentKey()).isEqualTo("foo"); + assertThat(issue.ruleKey()).isEqualTo(RuleKey.of("repo", "rule")); + assertThat(issue.severity()).isEqualTo("MAJOR"); + assertThat(issue.message()).isEqualTo("msg"); + assertThat(issue.line()).isEqualTo(1); + assertThat(issue.effortToFix()).isEqualTo(2.0); + assertThat(issue.status()).isEqualTo("RESOLVED"); + assertThat(issue.resolution()).isEqualTo("FIXED"); + assertThat(issue.reporter()).isEqualTo("toto"); + assertThat(issue.assignee()).isEqualTo("tata"); + assertThat(issue.isNew()).isTrue(); + assertThat(issue.attribute("foo")).isNull(); + assertThat(issue.creationDate()).isEqualTo(creationDate); + assertThat(issue.language()).isNull(); + assertThat(issue.updateDate()).isNull(); + assertThat(issue.closeDate()).isNull(); + assertThat(issue.authorLogin()).isNull(); + assertThat(issue.actionPlanKey()).isNull(); + assertThat(issue.comments()).isEmpty(); + assertThat(issue.debt()).isNull(); + assertThat(issue.projectKey()).isNull(); + assertThat(issue.projectUuid()).isNull(); + assertThat(issue.componentUuid()).isNull(); + assertThat(issue.tags()).isEmpty(); + + assertThat(issue).isNotEqualTo(null); + assertThat(issue).isNotEqualTo("Foo"); + assertThat(issue).isEqualTo(new TrackedIssueAdapter(trackedIssue)); + assertThat(issue.hashCode()).isEqualTo(trackedIssue.key().hashCode()); + assertThat(issue).isNotEqualTo(new TrackedIssueAdapter(new TrackedIssue() + .setKey("another"))); + } + +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/ignore/EnforceIssuesFilterTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/ignore/EnforceIssuesFilterTest.java new file mode 100644 index 00000000000..28df728657d --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/ignore/EnforceIssuesFilterTest.java @@ -0,0 +1,149 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.issue.ignore; + +import org.sonar.api.scan.issue.filter.FilterableIssue; + +import com.google.common.collect.ImmutableList; +import org.junit.Before; +import org.junit.Test; +import org.sonar.api.scan.issue.filter.IssueFilterChain; +import org.sonar.api.rule.RuleKey; +import org.sonar.api.utils.WildcardPattern; +import org.sonar.batch.issue.ignore.pattern.IssueInclusionPatternInitializer; +import org.sonar.batch.issue.ignore.pattern.IssuePattern; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.when; + +public class EnforceIssuesFilterTest { + + private IssueInclusionPatternInitializer exclusionPatternInitializer; + private EnforceIssuesFilter ignoreFilter; + private FilterableIssue issue; + private IssueFilterChain chain; + + @Before + public void init() { + exclusionPatternInitializer = mock(IssueInclusionPatternInitializer.class); + issue = mock(FilterableIssue.class); + chain = mock(IssueFilterChain.class); + when(chain.accept(issue)).thenReturn(true); + + ignoreFilter = new EnforceIssuesFilter(exclusionPatternInitializer); + } + + @Test + public void shouldPassToChainIfNoConfiguredPatterns() { + assertThat(ignoreFilter.accept(issue, chain)).isTrue(); + verify(chain).accept(issue); + } + + @Test + public void shouldPassToChainIfRuleDoesNotMatch() { + String rule = "rule"; + RuleKey ruleKey = mock(RuleKey.class); + when(ruleKey.toString()).thenReturn(rule); + when(issue.ruleKey()).thenReturn(ruleKey); + + IssuePattern matching = mock(IssuePattern.class); + WildcardPattern rulePattern = mock(WildcardPattern.class); + when(matching.getRulePattern()).thenReturn(rulePattern); + when(rulePattern.match(rule)).thenReturn(false); + when(exclusionPatternInitializer.getMulticriteriaPatterns()).thenReturn(ImmutableList.of(matching)); + + assertThat(ignoreFilter.accept(issue, chain)).isTrue(); + verify(chain).accept(issue); + } + + @Test + public void shouldAcceptIssueIfFullyMatched() { + String rule = "rule"; + String path = "org/sonar/api/Issue.java"; + String componentKey = "org.sonar.api.Issue"; + RuleKey ruleKey = mock(RuleKey.class); + when(ruleKey.toString()).thenReturn(rule); + when(issue.ruleKey()).thenReturn(ruleKey); + when(issue.componentKey()).thenReturn(componentKey); + + IssuePattern matching = mock(IssuePattern.class); + WildcardPattern rulePattern = mock(WildcardPattern.class); + when(matching.getRulePattern()).thenReturn(rulePattern); + when(rulePattern.match(rule)).thenReturn(true); + WildcardPattern pathPattern = mock(WildcardPattern.class); + when(matching.getResourcePattern()).thenReturn(pathPattern); + when(pathPattern.match(path)).thenReturn(true); + when(exclusionPatternInitializer.getMulticriteriaPatterns()).thenReturn(ImmutableList.of(matching)); + when(exclusionPatternInitializer.getPathForComponent(componentKey)).thenReturn(path); + + assertThat(ignoreFilter.accept(issue, chain)).isTrue(); + verifyZeroInteractions(chain); + } + + @Test + public void shouldRefuseIssueIfRuleMatchesButNotPath() { + String rule = "rule"; + String path = "org/sonar/api/Issue.java"; + String componentKey = "org.sonar.api.Issue"; + RuleKey ruleKey = mock(RuleKey.class); + when(ruleKey.toString()).thenReturn(rule); + when(issue.ruleKey()).thenReturn(ruleKey); + when(issue.componentKey()).thenReturn(componentKey); + + IssuePattern matching = mock(IssuePattern.class); + WildcardPattern rulePattern = mock(WildcardPattern.class); + when(matching.getRulePattern()).thenReturn(rulePattern); + when(rulePattern.match(rule)).thenReturn(true); + WildcardPattern pathPattern = mock(WildcardPattern.class); + when(matching.getResourcePattern()).thenReturn(pathPattern); + when(pathPattern.match(path)).thenReturn(false); + when(exclusionPatternInitializer.getMulticriteriaPatterns()).thenReturn(ImmutableList.of(matching)); + when(exclusionPatternInitializer.getPathForComponent(componentKey)).thenReturn(path); + + assertThat(ignoreFilter.accept(issue, chain)).isFalse(); + verifyZeroInteractions(chain); + } + + @Test + public void shouldRefuseIssueIfRuleMatchesAndPathUnknown() { + String rule = "rule"; + String path = "org/sonar/api/Issue.java"; + String componentKey = "org.sonar.api.Issue"; + RuleKey ruleKey = mock(RuleKey.class); + when(ruleKey.toString()).thenReturn(rule); + when(issue.ruleKey()).thenReturn(ruleKey); + when(issue.componentKey()).thenReturn(componentKey); + + IssuePattern matching = mock(IssuePattern.class); + WildcardPattern rulePattern = mock(WildcardPattern.class); + when(matching.getRulePattern()).thenReturn(rulePattern); + when(rulePattern.match(rule)).thenReturn(true); + WildcardPattern pathPattern = mock(WildcardPattern.class); + when(matching.getResourcePattern()).thenReturn(pathPattern); + when(pathPattern.match(path)).thenReturn(false); + when(exclusionPatternInitializer.getMulticriteriaPatterns()).thenReturn(ImmutableList.of(matching)); + when(exclusionPatternInitializer.getPathForComponent(componentKey)).thenReturn(null); + + assertThat(ignoreFilter.accept(issue, chain)).isFalse(); + verifyZeroInteractions(chain); + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/ignore/IgnoreIssuesFilterTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/ignore/IgnoreIssuesFilterTest.java new file mode 100644 index 00000000000..6cf431e215c --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/ignore/IgnoreIssuesFilterTest.java @@ -0,0 +1,67 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.issue.ignore; + +import org.sonar.api.scan.issue.filter.FilterableIssue; + +import org.junit.Before; +import org.junit.Test; +import org.sonar.api.scan.issue.filter.IssueFilterChain; +import org.sonar.batch.issue.ignore.pattern.IssueExclusionPatternInitializer; +import org.sonar.batch.issue.ignore.pattern.IssuePattern; +import org.sonar.batch.issue.ignore.pattern.PatternMatcher; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class IgnoreIssuesFilterTest { + + private IssueExclusionPatternInitializer exclusionPatternInitializer; + private PatternMatcher exclusionPatternMatcher; + private IgnoreIssuesFilter ignoreFilter; + private FilterableIssue issue; + private IssueFilterChain chain; + + @Before + public void init() { + exclusionPatternMatcher = mock(PatternMatcher.class); + exclusionPatternInitializer = mock(IssueExclusionPatternInitializer.class); + when(exclusionPatternInitializer.getPatternMatcher()).thenReturn(exclusionPatternMatcher); + issue = mock(FilterableIssue.class); + chain = mock(IssueFilterChain.class); + when(chain.accept(issue)).thenReturn(true); + + ignoreFilter = new IgnoreIssuesFilter(exclusionPatternInitializer); + } + + @Test + public void shouldPassToChainIfMatcherHasNoPatternForIssue() { + when(exclusionPatternMatcher.getMatchingPattern(issue)).thenReturn(null); + + assertThat(ignoreFilter.accept(issue, chain)).isTrue(); + } + + @Test + public void shouldAcceptOrRefuseIfMatcherHasPatternForIssue() { + when(exclusionPatternMatcher.getMatchingPattern(issue)).thenReturn(mock(IssuePattern.class)); + + assertThat(ignoreFilter.accept(issue, chain)).isFalse(); + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/ignore/pattern/IssueExclusionPatternInitializerTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/ignore/pattern/IssueExclusionPatternInitializerTest.java new file mode 100644 index 00000000000..13e1646bb9d --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/ignore/pattern/IssueExclusionPatternInitializerTest.java @@ -0,0 +1,141 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.issue.ignore.pattern; + + +import org.junit.Before; +import org.junit.Test; +import org.sonar.api.config.PropertyDefinitions; +import org.sonar.api.config.Settings; +import org.sonar.api.utils.SonarException; +import org.sonar.core.config.IssueExclusionProperties; + +import static org.assertj.core.api.Assertions.assertThat; + +public class IssueExclusionPatternInitializerTest { + + private IssueExclusionPatternInitializer patternsInitializer; + + private Settings settings; + + @Before + public void init() { + settings = new Settings(new PropertyDefinitions(IssueExclusionProperties.all())); + patternsInitializer = new IssueExclusionPatternInitializer(settings); + } + + @Test + public void testNoConfiguration() { + patternsInitializer.initPatterns(); + assertThat(patternsInitializer.hasConfiguredPatterns()).isFalse(); + assertThat(patternsInitializer.getMulticriteriaPatterns().size()).isEqualTo(0); + } + + @Test + public void shouldHavePatternsBasedOnMulticriteriaPattern() { + settings.setProperty("sonar.issue.ignore" + ".multicriteria", "1,2"); + settings.setProperty("sonar.issue.ignore" + ".multicriteria" + ".1." + "resourceKey", "org/foo/Bar.java"); + settings.setProperty("sonar.issue.ignore" + ".multicriteria" + ".1." + "ruleKey", "*"); + settings.setProperty("sonar.issue.ignore" + ".multicriteria" + ".2." + "resourceKey", "org/foo/Hello.java"); + settings.setProperty("sonar.issue.ignore" + ".multicriteria" + ".2." + "ruleKey", "checkstyle:MagicNumber"); + patternsInitializer.initPatterns(); + + assertThat(patternsInitializer.hasConfiguredPatterns()).isTrue(); + assertThat(patternsInitializer.hasFileContentPattern()).isFalse(); + assertThat(patternsInitializer.hasMulticriteriaPatterns()).isTrue(); + assertThat(patternsInitializer.getMulticriteriaPatterns().size()).isEqualTo(2); + assertThat(patternsInitializer.getBlockPatterns().size()).isEqualTo(0); + assertThat(patternsInitializer.getAllFilePatterns().size()).isEqualTo(0); + + patternsInitializer.initializePatternsForPath("org/foo/Bar.java", "org.foo.Bar"); + patternsInitializer.initializePatternsForPath("org/foo/Baz.java", "org.foo.Baz"); + patternsInitializer.initializePatternsForPath("org/foo/Hello.java", "org.foo.Hello"); + + assertThat(patternsInitializer.getPatternMatcher().getPatternsForComponent("org.foo.Bar")).hasSize(1); + assertThat(patternsInitializer.getPatternMatcher().getPatternsForComponent("org.foo.Baz")).hasSize(0); + assertThat(patternsInitializer.getPatternMatcher().getPatternsForComponent("org.foo.Hello")).hasSize(1); + + } + + @Test(expected = SonarException.class) + public void shouldLogInvalidResourceKey() { + settings.setProperty("sonar.issue.ignore" + ".multicriteria", "1"); + settings.setProperty("sonar.issue.ignore" + ".multicriteria" + ".1." + "resourceKey", ""); + settings.setProperty("sonar.issue.ignore" + ".multicriteria" + ".1." + "ruleKey", "*"); + patternsInitializer.initPatterns(); + } + + @Test(expected = SonarException.class) + public void shouldLogInvalidRuleKey() { + settings.setProperty("sonar.issue.ignore" + ".multicriteria", "1"); + settings.setProperty("sonar.issue.ignore" + ".multicriteria" + ".1." + "resourceKey", "*"); + settings.setProperty("sonar.issue.ignore" + ".multicriteria" + ".1." + "ruleKey", ""); + patternsInitializer.initPatterns(); + } + + @Test + public void shouldReturnBlockPattern() { + settings.setProperty(IssueExclusionProperties.PATTERNS_BLOCK_KEY, "1,2,3"); + settings.setProperty(IssueExclusionProperties.PATTERNS_BLOCK_KEY + ".1." + IssueExclusionProperties.BEGIN_BLOCK_REGEXP, "// SONAR-OFF"); + settings.setProperty(IssueExclusionProperties.PATTERNS_BLOCK_KEY + ".1." + IssueExclusionProperties.END_BLOCK_REGEXP, "// SONAR-ON"); + settings.setProperty(IssueExclusionProperties.PATTERNS_BLOCK_KEY + ".2." + IssueExclusionProperties.BEGIN_BLOCK_REGEXP, "// FOO-OFF"); + settings.setProperty(IssueExclusionProperties.PATTERNS_BLOCK_KEY + ".2." + IssueExclusionProperties.END_BLOCK_REGEXP, "// FOO-ON"); + settings.setProperty(IssueExclusionProperties.PATTERNS_BLOCK_KEY + ".3." + IssueExclusionProperties.BEGIN_BLOCK_REGEXP, "// IGNORE-TO-EOF"); + settings.setProperty(IssueExclusionProperties.PATTERNS_BLOCK_KEY + ".3." + IssueExclusionProperties.END_BLOCK_REGEXP, ""); + patternsInitializer.loadFileContentPatterns(); + + assertThat(patternsInitializer.hasConfiguredPatterns()).isTrue(); + assertThat(patternsInitializer.hasFileContentPattern()).isTrue(); + assertThat(patternsInitializer.hasMulticriteriaPatterns()).isFalse(); + assertThat(patternsInitializer.getMulticriteriaPatterns().size()).isEqualTo(0); + assertThat(patternsInitializer.getBlockPatterns().size()).isEqualTo(3); + assertThat(patternsInitializer.getAllFilePatterns().size()).isEqualTo(0); + } + + @Test(expected = SonarException.class) + public void shouldLogInvalidStartBlockPattern() { + settings.setProperty(IssueExclusionProperties.PATTERNS_BLOCK_KEY, "1"); + settings.setProperty(IssueExclusionProperties.PATTERNS_BLOCK_KEY + ".1." + IssueExclusionProperties.BEGIN_BLOCK_REGEXP, ""); + settings.setProperty(IssueExclusionProperties.PATTERNS_BLOCK_KEY + ".1." + IssueExclusionProperties.END_BLOCK_REGEXP, "// SONAR-ON"); + patternsInitializer.loadFileContentPatterns(); + } + + @Test + public void shouldReturnAllFilePattern() { + settings.setProperty(IssueExclusionProperties.PATTERNS_ALLFILE_KEY, "1,2"); + settings.setProperty(IssueExclusionProperties.PATTERNS_ALLFILE_KEY + ".1." + IssueExclusionProperties.FILE_REGEXP, "@SONAR-IGNORE-ALL"); + settings.setProperty(IssueExclusionProperties.PATTERNS_ALLFILE_KEY + ".2." + IssueExclusionProperties.FILE_REGEXP, "//FOO-IGNORE-ALL"); + patternsInitializer.loadFileContentPatterns(); + + assertThat(patternsInitializer.hasConfiguredPatterns()).isTrue(); + assertThat(patternsInitializer.hasFileContentPattern()).isTrue(); + assertThat(patternsInitializer.hasMulticriteriaPatterns()).isFalse(); + assertThat(patternsInitializer.getMulticriteriaPatterns().size()).isEqualTo(0); + assertThat(patternsInitializer.getBlockPatterns().size()).isEqualTo(0); + assertThat(patternsInitializer.getAllFilePatterns().size()).isEqualTo(2); + } + + @Test(expected = SonarException.class) + public void shouldLogInvalidAllFilePattern() { + settings.setProperty(IssueExclusionProperties.PATTERNS_ALLFILE_KEY, "1"); + settings.setProperty(IssueExclusionProperties.PATTERNS_ALLFILE_KEY + ".1." + IssueExclusionProperties.FILE_REGEXP, ""); + patternsInitializer.loadFileContentPatterns(); + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/ignore/pattern/IssueInclusionPatternInitializerTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/ignore/pattern/IssueInclusionPatternInitializerTest.java new file mode 100644 index 00000000000..25c74587522 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/ignore/pattern/IssueInclusionPatternInitializerTest.java @@ -0,0 +1,70 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.issue.ignore.pattern; + +import org.junit.Before; +import org.junit.Test; +import org.sonar.api.config.PropertyDefinitions; +import org.sonar.api.config.Settings; +import org.sonar.core.config.IssueExclusionProperties; + +import static org.assertj.core.api.Assertions.assertThat; + +public class IssueInclusionPatternInitializerTest { + + private IssueInclusionPatternInitializer patternsInitializer; + + private Settings settings; + + @Before + public void init() { + settings = new Settings(new PropertyDefinitions(IssueExclusionProperties.all())); + patternsInitializer = new IssueInclusionPatternInitializer(settings); + } + + @Test + public void testNoConfiguration() { + patternsInitializer.initPatterns(); + assertThat(patternsInitializer.hasConfiguredPatterns()).isFalse(); + } + + @Test + public void shouldHavePatternsBasedOnMulticriteriaPattern() { + settings.setProperty("sonar.issue.enforce" + ".multicriteria", "1,2"); + settings.setProperty("sonar.issue.enforce" + ".multicriteria" + ".1." + "resourceKey", "org/foo/Bar.java"); + settings.setProperty("sonar.issue.enforce" + ".multicriteria" + ".1." + "ruleKey", "*"); + settings.setProperty("sonar.issue.enforce" + ".multicriteria" + ".2." + "resourceKey", "org/foo/Hello.java"); + settings.setProperty("sonar.issue.enforce" + ".multicriteria" + ".2." + "ruleKey", "checkstyle:MagicNumber"); + patternsInitializer.initPatterns(); + + assertThat(patternsInitializer.hasConfiguredPatterns()).isTrue(); + assertThat(patternsInitializer.hasMulticriteriaPatterns()).isTrue(); + assertThat(patternsInitializer.getMulticriteriaPatterns().size()).isEqualTo(2); + + patternsInitializer.initializePatternsForPath("org/foo/Bar.java", "org.foo.Bar"); + patternsInitializer.initializePatternsForPath("org/foo/Baz.java", "org.foo.Baz"); + patternsInitializer.initializePatternsForPath("org/foo/Hello.java", "org.foo.Hello"); + + assertThat(patternsInitializer.getPathForComponent("org.foo.Bar")).isEqualTo("org/foo/Bar.java"); + assertThat(patternsInitializer.getPathForComponent("org.foo.Baz")).isEqualTo("org/foo/Baz.java"); + assertThat(patternsInitializer.getPathForComponent("org.foo.Hello")).isEqualTo("org/foo/Hello.java"); + } + +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/ignore/pattern/IssuePatternTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/ignore/pattern/IssuePatternTest.java new file mode 100644 index 00000000000..e67111d0f9c --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/ignore/pattern/IssuePatternTest.java @@ -0,0 +1,114 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.issue.ignore.pattern; + +import org.sonar.api.scan.issue.filter.FilterableIssue; + +import org.junit.Test; +import org.sonar.api.rule.RuleKey; +import org.sonar.api.rules.Rule; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class IssuePatternTest { + + @Test + public void shouldMatchLines() { + IssuePattern pattern = new IssuePattern("*", "*"); + pattern.addLine(12).addLine(15).addLineRange(20, 25); + + assertThat(pattern.matchLine(3)).isFalse(); + assertThat(pattern.matchLine(12)).isTrue(); + assertThat(pattern.matchLine(14)).isFalse(); + assertThat(pattern.matchLine(21)).isTrue(); + assertThat(pattern.matchLine(6599)).isFalse(); + } + + @Test + public void shouldMatchJavaFile() { + String javaFile = "org.foo.Bar"; + assertThat(new IssuePattern("org.foo.Bar", "*").matchResource(javaFile)).isTrue(); + assertThat(new IssuePattern("org.foo.*", "*").matchResource(javaFile)).isTrue(); + assertThat(new IssuePattern("*Bar", "*").matchResource(javaFile)).isTrue(); + assertThat(new IssuePattern("*", "*").matchResource(javaFile)).isTrue(); + assertThat(new IssuePattern("org.*.?ar", "*").matchResource(javaFile)).isTrue(); + + assertThat(new IssuePattern("org.other.Hello", "*").matchResource(javaFile)).isFalse(); + assertThat(new IssuePattern("org.foo.Hello", "*").matchResource(javaFile)).isFalse(); + assertThat(new IssuePattern("org.*.??ar", "*").matchResource(javaFile)).isFalse(); + assertThat(new IssuePattern("org.*.??ar", "*").matchResource(null)).isFalse(); + assertThat(new IssuePattern("org.*.??ar", "*").matchResource("plop")).isFalse(); + } + + @Test + public void shouldMatchRule() { + RuleKey rule = Rule.create("checkstyle", "IllegalRegexp", "").ruleKey(); + assertThat(new IssuePattern("*", "*").matchRule(rule)).isTrue(); + assertThat(new IssuePattern("*", "checkstyle:*").matchRule(rule)).isTrue(); + assertThat(new IssuePattern("*", "checkstyle:IllegalRegexp").matchRule(rule)).isTrue(); + assertThat(new IssuePattern("*", "checkstyle:Illegal*").matchRule(rule)).isTrue(); + assertThat(new IssuePattern("*", "*:*Illegal*").matchRule(rule)).isTrue(); + + assertThat(new IssuePattern("*", "pmd:IllegalRegexp").matchRule(rule)).isFalse(); + assertThat(new IssuePattern("*", "pmd:*").matchRule(rule)).isFalse(); + assertThat(new IssuePattern("*", "*:Foo*IllegalRegexp").matchRule(rule)).isFalse(); + } + + @Test + public void shouldMatchViolation() { + Rule rule = Rule.create("checkstyle", "IllegalRegexp", ""); + String javaFile = "org.foo.Bar"; + + IssuePattern pattern = new IssuePattern("*", "*"); + pattern.addLine(12); + + assertThat(pattern.match(create(rule, javaFile, null))).isFalse(); + assertThat(pattern.match(create(rule, javaFile, 12))).isTrue(); + assertThat(pattern.match(create((Rule) null, javaFile, 5))).isFalse(); + assertThat(pattern.match(create(rule, null, null))).isFalse(); + assertThat(pattern.match(create((Rule) null, null, null))).isFalse(); + } + + private FilterableIssue create(Rule rule, String component, Integer line) { + FilterableIssue mockIssue = mock(FilterableIssue.class); + RuleKey ruleKey = null; + if (rule != null) { + ruleKey = rule.ruleKey(); + } + when(mockIssue.ruleKey()).thenReturn(ruleKey); + when(mockIssue.componentKey()).thenReturn(component); + when(mockIssue.line()).thenReturn(line); + return mockIssue; + } + + @Test + public void shouldNotMatchNullRule() { + assertThat(new IssuePattern("*", "*").matchRule(null)).isFalse(); + } + + @Test + public void shouldPrintPatternToString() { + IssuePattern pattern = new IssuePattern("*", "checkstyle:*"); + + assertThat(pattern.toString()).isEqualTo( + "IssuePattern[resourcePattern=*,rulePattern=checkstyle:*,lines=[],lineRanges=[],beginBlockRegexp=<null>,endBlockRegexp=<null>,allFileRegexp=<null>,checkLines=true]"); + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/ignore/pattern/LineRangeTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/ignore/pattern/LineRangeTest.java new file mode 100644 index 00000000000..8af95b5d4ad --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/ignore/pattern/LineRangeTest.java @@ -0,0 +1,72 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.issue.ignore.pattern; + +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class LineRangeTest { + + @Test(expected = IllegalArgumentException.class) + public void lineRangeShouldBeOrdered() { + new LineRange(25, 12); + } + + @Test + public void shouldConvertLineRangeToLines() { + LineRange range = new LineRange(12, 15); + + assertThat(range.toLines()).containsOnly(12, 13, 14, 15); + } + + @Test + public void shouldTestInclusionInRangeOfLines() { + LineRange range = new LineRange(12, 15); + + assertThat(range.in(3)).isFalse(); + assertThat(range.in(12)).isTrue(); + assertThat(range.in(13)).isTrue(); + assertThat(range.in(14)).isTrue(); + assertThat(range.in(15)).isTrue(); + assertThat(range.in(16)).isFalse(); + } + + @Test + public void testToString() throws Exception { + assertThat(new LineRange(12, 15).toString()).isEqualTo("[12-15]"); + } + + @Test + public void testEquals() throws Exception { + LineRange range = new LineRange(12, 15); + assertThat(range).isEqualTo(range); + assertThat(range).isEqualTo(new LineRange(12, 15)); + assertThat(range).isNotEqualTo(new LineRange(12, 2000)); + assertThat(range).isNotEqualTo(new LineRange(1000, 2000)); + assertThat(range).isNotEqualTo(null); + assertThat(range).isNotEqualTo(new StringBuffer()); + } + + @Test + public void testHashCode() throws Exception { + assertThat(new LineRange(12, 15).hashCode()).isEqualTo(new LineRange(12, 15).hashCode()); + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/ignore/pattern/PatternDecoderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/ignore/pattern/PatternDecoderTest.java new file mode 100644 index 00000000000..3501d1a2326 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/ignore/pattern/PatternDecoderTest.java @@ -0,0 +1,187 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.issue.ignore.pattern; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.sonar.api.utils.SonarException; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +public class PatternDecoderTest { + + private PatternDecoder decoder = new PatternDecoder(); + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Test + public void shouldReadString() { + String patternsList = "# a comment followed by a blank line\n\n" + + "# suppress all violations\n" + + "*;*;*\n\n" + + "# exclude a Java file\n" + + "com.foo.Bar;*;*\n\n" + + "# exclude a Java package\n" + + "com.foo.*;*;*\n\n" + + "# exclude a specific rule\n" + + "*;checkstyle:IllegalRegexp;*\n\n" + + "# exclude a specific rule on a specific file\n" + + "com.foo.Bar;checkstyle:IllegalRegexp;*\n"; + List<IssuePattern> patterns = decoder.decode(patternsList); + + assertThat(patterns).hasSize(5); + } + + @Test + public void shouldCheckFormatOfResource() { + assertThat(PatternDecoder.isResource("")).isFalse(); + assertThat(PatternDecoder.isResource("*")).isTrue(); + assertThat(PatternDecoder.isResource("com.foo.*")).isTrue(); + } + + @Test + public void shouldCheckFormatOfRule() { + assertThat(PatternDecoder.isRule("")).isFalse(); + assertThat(PatternDecoder.isRule("*")).isTrue(); + assertThat(PatternDecoder.isRule("com.foo.*")).isTrue(); + } + + @Test + public void shouldCheckFormatOfLinesRange() { + assertThat(PatternDecoder.isLinesRange("")).isFalse(); + assertThat(PatternDecoder.isLinesRange(" ")).isFalse(); + assertThat(PatternDecoder.isLinesRange("12")).isFalse(); + assertThat(PatternDecoder.isLinesRange("12,212")).isFalse(); + + assertThat(PatternDecoder.isLinesRange("*")).isTrue(); + assertThat(PatternDecoder.isLinesRange("[]")).isTrue(); + assertThat(PatternDecoder.isLinesRange("[13]")).isTrue(); + assertThat(PatternDecoder.isLinesRange("[13,24]")).isTrue(); + assertThat(PatternDecoder.isLinesRange("[13,24,25-500]")).isTrue(); + assertThat(PatternDecoder.isLinesRange("[24-65]")).isTrue(); + assertThat(PatternDecoder.isLinesRange("[13,24-65,84-89,122]")).isTrue(); + } + + @Test + public void shouldReadStarPatterns() { + IssuePattern pattern = decoder.decodeLine("*;*;*"); + + assertThat(pattern.getResourcePattern().toString()).isEqualTo("*"); + assertThat(pattern.getRulePattern().toString()).isEqualTo("*"); + assertThat(pattern.isCheckLines()).isFalse(); + } + + @Test + public void shouldReadLineIds() { + IssuePattern pattern = decoder.decodeLine("*;*;[10,25,98]"); + + assertThat(pattern.isCheckLines()).isTrue(); + assertThat(pattern.getAllLines()).containsOnly(10, 25, 98); + } + + @Test + public void shouldReadRangeOfLineIds() { + IssuePattern pattern = decoder.decodeLine("*;*;[10-12,25,97-100]"); + + assertThat(pattern.isCheckLines()).isTrue(); + assertThat(pattern.getAllLines()).containsOnly(10, 11, 12, 25, 97, 98, 99, 100); + } + + @Test + public void shouldNotExcludeLines() { + // [] is different than * + // - all violations are excluded on * + // * no violations are excluded on [] + IssuePattern pattern = decoder.decodeLine("*;*;[]"); + + assertThat(pattern.isCheckLines()).isTrue(); + assertThat(pattern.getAllLines()).isEmpty(); + } + + @Test + public void shouldReadBlockPattern() { + IssuePattern pattern = decoder.decodeLine("SONAR-OFF;SONAR-ON"); + + assertThat(pattern.getResourcePattern()).isNull(); + assertThat(pattern.getBeginBlockRegexp()).isEqualTo("SONAR-OFF"); + assertThat(pattern.getEndBlockRegexp()).isEqualTo("SONAR-ON"); + } + + @Test + public void shouldReadAllFilePattern() { + IssuePattern pattern = decoder.decodeLine("SONAR-ALL-OFF"); + + assertThat(pattern.getResourcePattern()).isNull(); + assertThat(pattern.getAllFileRegexp()).isEqualTo("SONAR-ALL-OFF"); + } + + @Test + public void shouldFailToReadUncorrectLine1() { + thrown.expect(SonarException.class); + thrown.expectMessage("Exclusions > Issues : Invalid format. The following line has more than 3 fields separated by comma"); + + decoder.decode(";;;;"); + } + + @Test + public void shouldFailToReadUncorrectLine3() { + thrown.expect(SonarException.class); + thrown.expectMessage("Exclusions > Issues : Invalid format. The first field does not define a resource pattern"); + + decoder.decode(";*;*"); + } + + @Test + public void shouldFailToReadUncorrectLine4() { + thrown.expect(SonarException.class); + thrown.expectMessage("Exclusions > Issues : Invalid format. The second field does not define a rule pattern"); + + decoder.decode("*;;*"); + } + + @Test + public void shouldFailToReadUncorrectLine5() { + thrown.expect(SonarException.class); + thrown.expectMessage("Exclusions > Issues : Invalid format. The third field does not define a range of lines"); + + decoder.decode("*;*;blabla"); + } + + @Test + public void shouldFailToReadUncorrectLine6() { + thrown.expect(SonarException.class); + thrown.expectMessage("Exclusions > Issues : Invalid format. The first field does not define a regular expression"); + + decoder.decode(";ON"); + } + + @Test + public void shouldAcceptEmptyEndBlockRegexp() { + IssuePattern pattern = decoder.decodeLine("OFF;"); + + assertThat(pattern.getResourcePattern()).isNull(); + assertThat(pattern.getBeginBlockRegexp()).isEqualTo("OFF"); + assertThat(pattern.getEndBlockRegexp()).isEmpty(); + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/ignore/pattern/PatternMatcherTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/ignore/pattern/PatternMatcherTest.java new file mode 100644 index 00000000000..1d69a30c371 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/ignore/pattern/PatternMatcherTest.java @@ -0,0 +1,119 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.issue.ignore.pattern; + +import org.sonar.api.scan.issue.filter.FilterableIssue; + +import com.google.common.collect.Sets; +import org.junit.Before; +import org.junit.Test; +import org.sonar.api.rule.RuleKey; +import org.sonar.api.rules.Rule; + +import java.util.Set; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class PatternMatcherTest { + + public static final Rule CHECKSTYLE_RULE = Rule.create("checkstyle", "MagicNumber", ""); + public static final String JAVA_FILE = "org.foo.Hello"; + + private PatternMatcher patternMatcher; + + @Before + public void setUp() { + patternMatcher = new PatternMatcher(); + } + + @Test + public void shouldReturnExtraPatternForResource() { + String file = "foo"; + patternMatcher.addPatternToExcludeResource(file); + + IssuePattern extraPattern = patternMatcher.getPatternsForComponent(file).iterator().next(); + assertThat(extraPattern.matchResource(file)).isTrue(); + assertThat(extraPattern.isCheckLines()).isFalse(); + } + + @Test + public void shouldReturnExtraPatternForLinesOfResource() { + String file = "foo"; + Set<LineRange> lineRanges = Sets.newHashSet(); + lineRanges.add(new LineRange(25, 28)); + patternMatcher.addPatternToExcludeLines(file, lineRanges); + + IssuePattern extraPattern = patternMatcher.getPatternsForComponent(file).iterator().next(); + assertThat(extraPattern.matchResource(file)).isTrue(); + assertThat(extraPattern.getAllLines()).isEqualTo(Sets.newHashSet(25, 26, 27, 28)); + } + + @Test + public void shouldHaveNoMatcherIfNoneDefined() { + assertThat(patternMatcher.getMatchingPattern(create(CHECKSTYLE_RULE, JAVA_FILE, null))).isNull(); + } + + @Test + public void shouldMatchWithStandardPatterns() { + patternMatcher.addPatternForComponent(JAVA_FILE, createPattern("org.foo.Hello;checkstyle:MagicNumber;[15-200]")); + + assertThat(patternMatcher.getMatchingPattern(create(CHECKSTYLE_RULE, JAVA_FILE, 150))).isNotNull(); + } + + @Test + public void shouldNotMatchWithStandardPatterns() { + patternMatcher.addPatternForComponent(JAVA_FILE, createPattern("org.foo.Hello;checkstyle:MagicNumber;[15-200]")); + + assertThat(patternMatcher.getMatchingPattern(create(CHECKSTYLE_RULE, JAVA_FILE, 5))).isNull(); + } + + @Test + public void shouldMatchWithExtraPattern() { + patternMatcher.addPatternForComponent(JAVA_FILE, createPattern("org.foo.Hello;*;[15-200]")); + + assertThat(patternMatcher.getMatchingPattern(create(CHECKSTYLE_RULE, JAVA_FILE, 150))).isNotNull(); + } + + @Test + public void shouldNotMatchWithExtraPattern() { + patternMatcher.addPatternForComponent(JAVA_FILE, createPattern("org.foo.Hello;*;[15-200]")); + + assertThat(patternMatcher.getMatchingPattern(create(CHECKSTYLE_RULE, JAVA_FILE, 5))).isNull(); + } + + private FilterableIssue create(Rule rule, String component, Integer line) { + FilterableIssue mockIssue = mock(FilterableIssue.class); + RuleKey ruleKey = null; + if (rule != null) { + ruleKey = rule.ruleKey(); + } + when(mockIssue.ruleKey()).thenReturn(ruleKey); + when(mockIssue.componentKey()).thenReturn(component); + when(mockIssue.line()).thenReturn(line); + return mockIssue; + } + + private IssuePattern createPattern(String line) { + return new PatternDecoder().decode(line).get(0); + } + +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/ignore/scanner/IssueExclusionsLoaderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/ignore/scanner/IssueExclusionsLoaderTest.java new file mode 100644 index 00000000000..52a0e59cbbe --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/ignore/scanner/IssueExclusionsLoaderTest.java @@ -0,0 +1,157 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.issue.ignore.scanner; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.rules.TemporaryFolder; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.fs.internal.DefaultFileSystem; +import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.batch.issue.ignore.pattern.IssueExclusionPatternInitializer; +import org.sonar.batch.issue.ignore.pattern.IssueInclusionPatternInitializer; +import org.sonar.batch.issue.ignore.pattern.PatternMatcher; + +import java.io.File; +import java.io.IOException; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.when; + +public class IssueExclusionsLoaderTest { + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Rule + public TemporaryFolder temp = new TemporaryFolder(); + + @Mock + private IssueExclusionsRegexpScanner regexpScanner; + + @Mock + private IssueInclusionPatternInitializer inclusionPatternInitializer; + + @Mock + private IssueExclusionPatternInitializer exclusionPatternInitializer; + + @Mock + private PatternMatcher patternMatcher; + + private DefaultFileSystem fs; + private IssueExclusionsLoader scanner; + private File baseDir; + + @Before + public void before() throws Exception { + baseDir = temp.newFolder(); + fs = new DefaultFileSystem(baseDir.toPath()).setEncoding(UTF_8); + MockitoAnnotations.initMocks(this); + scanner = new IssueExclusionsLoader(regexpScanner, exclusionPatternInitializer, inclusionPatternInitializer, fs); + } + + @Test + public void testToString() throws Exception { + assertThat(scanner.toString()).isEqualTo("Issues Exclusions - Source Scanner"); + } + + @Test + public void shouldExecute() { + when(exclusionPatternInitializer.hasConfiguredPatterns()).thenReturn(true); + when(inclusionPatternInitializer.hasConfiguredPatterns()).thenReturn(true); + assertThat(scanner.shouldExecuteOnProject(null)).isTrue(); + + when(exclusionPatternInitializer.hasConfiguredPatterns()).thenReturn(true); + when(inclusionPatternInitializer.hasConfiguredPatterns()).thenReturn(false); + assertThat(scanner.shouldExecuteOnProject(null)).isTrue(); + + when(exclusionPatternInitializer.hasConfiguredPatterns()).thenReturn(false); + when(inclusionPatternInitializer.hasConfiguredPatterns()).thenReturn(true); + assertThat(scanner.shouldExecuteOnProject(null)).isTrue(); + + when(exclusionPatternInitializer.hasConfiguredPatterns()).thenReturn(false); + when(inclusionPatternInitializer.hasConfiguredPatterns()).thenReturn(false); + assertThat(scanner.shouldExecuteOnProject(null)).isFalse(); + + } + + @Test + public void shouldAnalyzeProject() throws IOException { + File javaFile1 = new File(baseDir, "src/main/java/Foo.java"); + fs.add(new DefaultInputFile("polop", "src/main/java/Foo.java") + .setType(InputFile.Type.MAIN)); + File javaTestFile1 = new File(baseDir, "src/test/java/FooTest.java"); + fs.add(new DefaultInputFile("polop", "src/test/java/FooTest.java") + .setType(InputFile.Type.TEST)); + + when(exclusionPatternInitializer.hasFileContentPattern()).thenReturn(true); + + scanner.execute(); + + verify(inclusionPatternInitializer).initializePatternsForPath("src/main/java/Foo.java", "polop:src/main/java/Foo.java"); + verify(inclusionPatternInitializer).initializePatternsForPath("src/test/java/FooTest.java", "polop:src/test/java/FooTest.java"); + verify(exclusionPatternInitializer).initializePatternsForPath("src/main/java/Foo.java", "polop:src/main/java/Foo.java"); + verify(exclusionPatternInitializer).initializePatternsForPath("src/test/java/FooTest.java", "polop:src/test/java/FooTest.java"); + verify(regexpScanner).scan("polop:src/main/java/Foo.java", javaFile1, UTF_8); + verify(regexpScanner).scan("polop:src/test/java/FooTest.java", javaTestFile1, UTF_8); + } + + @Test + public void shouldAnalyseFilesOnlyWhenRegexConfigured() { + File javaFile1 = new File(baseDir, "src/main/java/Foo.java"); + fs.add(new DefaultInputFile("polop", "src/main/java/Foo.java") + .setType(InputFile.Type.MAIN)); + File javaTestFile1 = new File(baseDir, "src/test/java/FooTest.java"); + fs.add(new DefaultInputFile("polop", "src/test/java/FooTest.java") + .setType(InputFile.Type.TEST)); + when(exclusionPatternInitializer.hasFileContentPattern()).thenReturn(false); + + scanner.execute(); + + verify(inclusionPatternInitializer).initializePatternsForPath("src/main/java/Foo.java", "polop:src/main/java/Foo.java"); + verify(inclusionPatternInitializer).initializePatternsForPath("src/test/java/FooTest.java", "polop:src/test/java/FooTest.java"); + verify(exclusionPatternInitializer).initializePatternsForPath("src/main/java/Foo.java", "polop:src/main/java/Foo.java"); + verify(exclusionPatternInitializer).initializePatternsForPath("src/test/java/FooTest.java", "polop:src/test/java/FooTest.java"); + verifyZeroInteractions(regexpScanner); + } + + @Test + public void shouldReportFailure() throws IOException { + File phpFile1 = new File(baseDir, "src/Foo.php"); + fs.add(new DefaultInputFile("polop", "src/Foo.php") + .setType(InputFile.Type.MAIN)); + + when(exclusionPatternInitializer.hasFileContentPattern()).thenReturn(true); + doThrow(new IOException("BUG")).when(regexpScanner).scan("polop:src/Foo.php", phpFile1, UTF_8); + + thrown.expect(IllegalStateException.class); + thrown.expectMessage("Unable to read the source file"); + + scanner.execute(); + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/ignore/scanner/IssueExclusionsRegexpScannerTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/ignore/scanner/IssueExclusionsRegexpScannerTest.java new file mode 100644 index 00000000000..f4aa4ebd9f1 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/ignore/scanner/IssueExclusionsRegexpScannerTest.java @@ -0,0 +1,168 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.issue.ignore.scanner; + +import com.google.common.collect.Sets; +import com.google.common.io.Resources; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.sonar.batch.issue.ignore.pattern.IssueExclusionPatternInitializer; +import org.sonar.batch.issue.ignore.pattern.IssuePattern; +import org.sonar.batch.issue.ignore.pattern.LineRange; +import org.sonar.batch.issue.ignore.pattern.PatternMatcher; + +import java.io.File; +import java.util.Arrays; +import java.util.Set; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +public class IssueExclusionsRegexpScannerTest { + + private IssueExclusionsRegexpScanner regexpScanner; + + private String javaFile; + @Mock + private IssueExclusionPatternInitializer patternsInitializer; + @Mock + private PatternMatcher patternMatcher; + @Mock + private IssuePattern allFilePattern; + @Mock + private IssuePattern blockPattern1; + @Mock + private IssuePattern blockPattern2; + + @Before + public void init() { + MockitoAnnotations.initMocks(this); + + when(allFilePattern.getAllFileRegexp()).thenReturn("@SONAR-IGNORE-ALL"); + when(blockPattern1.getBeginBlockRegexp()).thenReturn("// SONAR-OFF"); + when(blockPattern1.getEndBlockRegexp()).thenReturn("// SONAR-ON"); + when(blockPattern2.getBeginBlockRegexp()).thenReturn("// FOO-OFF"); + when(blockPattern2.getEndBlockRegexp()).thenReturn("// FOO-ON"); + when(patternsInitializer.getAllFilePatterns()).thenReturn(Arrays.asList(allFilePattern)); + when(patternsInitializer.getBlockPatterns()).thenReturn(Arrays.asList(blockPattern1, blockPattern2)); + when(patternsInitializer.getPatternMatcher()).thenReturn(patternMatcher); + + regexpScanner = new IssueExclusionsRegexpScanner(patternsInitializer); + verify(patternsInitializer, times(1)).getAllFilePatterns(); + verify(patternsInitializer, times(1)).getBlockPatterns(); + + javaFile = "org.sonar.test.MyFile"; + } + + @Test + public void shouldDoNothing() throws Exception { + regexpScanner.scan(javaFile, new File(Resources.getResource( + "org/sonar/batch/issue/ignore/scanner/IssueExclusionsRegexpScannerTest/file-with-no-regexp.txt").toURI()), UTF_8); + + verifyNoMoreInteractions(patternsInitializer); + } + + @Test + public void shouldAddPatternToExcludeFile() throws Exception { + regexpScanner.scan(javaFile, new File(Resources.getResource( + "org/sonar/batch/issue/ignore/scanner/IssueExclusionsRegexpScannerTest/file-with-single-regexp.txt").toURI()), UTF_8); + + verify(patternsInitializer).getPatternMatcher(); + verify(patternMatcher, times(1)).addPatternToExcludeResource(javaFile); + verifyNoMoreInteractions(patternsInitializer); + } + + @Test + public void shouldAddPatternToExcludeFileEvenIfAlsoDoubleRegexps() throws Exception { + regexpScanner.scan(javaFile, new File(Resources.getResource( + "org/sonar/batch/issue/ignore/scanner/IssueExclusionsRegexpScannerTest/file-with-single-regexp-and-double-regexp.txt").toURI()), UTF_8); + + verify(patternsInitializer).getPatternMatcher(); + verify(patternMatcher, times(1)).addPatternToExcludeResource(javaFile); + verifyNoMoreInteractions(patternsInitializer); + } + + @Test + public void shouldAddPatternToExcludeLines() throws Exception { + regexpScanner.scan(javaFile, new File(Resources.getResource( + "org/sonar/batch/issue/ignore/scanner/IssueExclusionsRegexpScannerTest/file-with-double-regexp.txt").toURI()), UTF_8); + + Set<LineRange> lineRanges = Sets.newHashSet(); + lineRanges.add(new LineRange(21, 25)); + verify(patternsInitializer).getPatternMatcher(); + verify(patternMatcher, times(1)).addPatternToExcludeLines(javaFile, lineRanges); + verifyNoMoreInteractions(patternsInitializer); + } + + @Test + public void shouldAddPatternToExcludeLinesTillTheEnd() throws Exception { + regexpScanner.scan(javaFile, new File(Resources.getResource( + "org/sonar/batch/issue/ignore/scanner/IssueExclusionsRegexpScannerTest/file-with-double-regexp-unfinished.txt").toURI()), UTF_8); + + Set<LineRange> lineRanges = Sets.newHashSet(); + lineRanges.add(new LineRange(21, 34)); + verify(patternsInitializer).getPatternMatcher(); + verify(patternMatcher, times(1)).addPatternToExcludeLines(javaFile, lineRanges); + verifyNoMoreInteractions(patternsInitializer); + } + + @Test + public void shouldAddPatternToExcludeSeveralLineRanges() throws Exception { + regexpScanner.scan(javaFile, new File(Resources.getResource( + "org/sonar/batch/issue/ignore/scanner/IssueExclusionsRegexpScannerTest/file-with-double-regexp-twice.txt").toURI()), UTF_8); + + Set<LineRange> lineRanges = Sets.newHashSet(); + lineRanges.add(new LineRange(21, 25)); + lineRanges.add(new LineRange(29, 33)); + verify(patternsInitializer).getPatternMatcher(); + verify(patternMatcher, times(1)).addPatternToExcludeLines(javaFile, lineRanges); + verifyNoMoreInteractions(patternsInitializer); + } + + @Test + public void shouldAddPatternToExcludeLinesWithWrongOrder() throws Exception { + regexpScanner.scan(javaFile, new File(Resources.getResource( + "org/sonar/batch/issue/ignore/scanner/IssueExclusionsRegexpScannerTest/file-with-double-regexp-wrong-order.txt").toURI()), UTF_8); + + Set<LineRange> lineRanges = Sets.newHashSet(); + lineRanges.add(new LineRange(25, 35)); + verify(patternsInitializer).getPatternMatcher(); + verify(patternMatcher, times(1)).addPatternToExcludeLines(javaFile, lineRanges); + verifyNoMoreInteractions(patternsInitializer); + } + + @Test + public void shouldAddPatternToExcludeLinesWithMess() throws Exception { + regexpScanner.scan(javaFile, new File(Resources.getResource( + "org/sonar/batch/issue/ignore/scanner/IssueExclusionsRegexpScannerTest/file-with-double-regexp-mess.txt").toURI()), UTF_8); + + Set<LineRange> lineRanges = Sets.newHashSet(); + lineRanges.add(new LineRange(21, 29)); + verify(patternsInitializer).getPatternMatcher(); + verify(patternMatcher, times(1)).addPatternToExcludeLines(javaFile, lineRanges); + verifyNoMoreInteractions(patternsInitializer); + } + +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/tracking/DefaultServerLineHashesLoaderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/tracking/DefaultServerLineHashesLoaderTest.java new file mode 100644 index 00000000000..7d8ab1d2319 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/tracking/DefaultServerLineHashesLoaderTest.java @@ -0,0 +1,89 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.issue.tracking; + +import org.sonar.batch.cache.WSLoader.LoadStrategy; +import org.sonar.batch.cache.WSLoaderResult; +import org.sonar.batch.cache.WSLoader; +import org.apache.commons.lang.mutable.MutableBoolean; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.sonar.api.utils.HttpDownloader; + +import java.net.URI; +import java.net.URISyntaxException; + +import static org.mockito.Matchers.any; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class DefaultServerLineHashesLoaderTest { + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Before + public void before() { + } + + @Test + public void should_download_source_from_ws_if_preview_mode() { + WSLoader wsLoader = mock(WSLoader.class); + when(wsLoader.loadString(anyString(), any(LoadStrategy.class))).thenReturn(new WSLoaderResult<>("ae12\n\n43fb", true)); + + ServerLineHashesLoader lastSnapshots = new DefaultServerLineHashesLoader(wsLoader); + + String[] hashes = lastSnapshots.getLineHashes("myproject:org/foo/Bar.c", null); + assertThat(hashes).containsOnly("ae12", "", "43fb"); + verify(wsLoader).loadString("/api/sources/hash?key=myproject%3Aorg%2Ffoo%2FBar.c", LoadStrategy.CACHE_FIRST); + } + + @Test + public void should_download_source_with_space_from_ws_if_preview_mode() { + WSLoader server = mock(WSLoader.class); + when(server.loadString(anyString(), any(LoadStrategy.class))).thenReturn(new WSLoaderResult<>("ae12\n\n43fb", true)); + + ServerLineHashesLoader lastSnapshots = new DefaultServerLineHashesLoader(server); + + MutableBoolean fromCache = new MutableBoolean(); + String[] hashes = lastSnapshots.getLineHashes("myproject:org/foo/Foo Bar.c", fromCache); + assertThat(fromCache.booleanValue()).isTrue(); + assertThat(hashes).containsOnly("ae12", "", "43fb"); + verify(server).loadString("/api/sources/hash?key=myproject%3Aorg%2Ffoo%2FFoo+Bar.c", LoadStrategy.CACHE_FIRST); + } + + @Test + public void should_fail_to_download_source_from_ws() throws URISyntaxException { + WSLoader server = mock(WSLoader.class); + when(server.loadString(anyString(), any(LoadStrategy.class))).thenThrow(new HttpDownloader.HttpException(new URI(""), 500)); + + ServerLineHashesLoader lastSnapshots = new DefaultServerLineHashesLoader(server); + + thrown.expect(HttpDownloader.HttpException.class); + lastSnapshots.getLineHashes("foo", null); + } + +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/tracking/RollingFileHashesTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/tracking/RollingFileHashesTest.java new file mode 100644 index 00000000000..8648fba48d1 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/tracking/RollingFileHashesTest.java @@ -0,0 +1,43 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.issue.tracking; + +import org.junit.Test; + +import static org.apache.commons.codec.digest.DigestUtils.md5Hex; +import static org.assertj.core.api.Assertions.assertThat; + +public class RollingFileHashesTest { + + @Test + public void test_equals() { + RollingFileHashes a = RollingFileHashes.create(FileHashes.create(new String[] {md5Hex("line0"), md5Hex("line1"), md5Hex("line2")}), 1); + RollingFileHashes b = RollingFileHashes.create(FileHashes.create(new String[] {md5Hex("line0"), md5Hex("line1"), md5Hex("line2"), md5Hex("line3")}), 1); + + assertThat(a.getHash(1) == b.getHash(1)).isTrue(); + assertThat(a.getHash(2) == b.getHash(2)).isTrue(); + assertThat(a.getHash(3) == b.getHash(3)).isFalse(); + + RollingFileHashes c = RollingFileHashes.create(FileHashes.create(new String[] {md5Hex("line-1"), md5Hex("line0"), md5Hex("line1"), md5Hex("line2"), md5Hex("line3")}), 1); + assertThat(a.getHash(1) == c.getHash(2)).isFalse(); + assertThat(a.getHash(2) == c.getHash(3)).isTrue(); + } + +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/tracking/SourceHashHolderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/tracking/SourceHashHolderTest.java new file mode 100644 index 00000000000..9acf02b3577 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/tracking/SourceHashHolderTest.java @@ -0,0 +1,119 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.issue.tracking; + +import org.apache.commons.io.FileUtils; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.mockito.Mockito; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.fs.internal.DefaultInputFile; + +import java.io.File; +import java.nio.charset.StandardCharsets; + +import static org.apache.commons.codec.digest.DigestUtils.md5Hex; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class SourceHashHolderTest { + + @Rule + public TemporaryFolder temp = new TemporaryFolder(); + + SourceHashHolder sourceHashHolder; + + ServerLineHashesLoader lastSnapshots; + DefaultInputFile file; + + private File ioFile; + + @Before + public void setUp() throws Exception { + lastSnapshots = mock(ServerLineHashesLoader.class); + file = mock(DefaultInputFile.class); + ioFile = temp.newFile(); + when(file.file()).thenReturn(ioFile); + when(file.path()).thenReturn(ioFile.toPath()); + when(file.lines()).thenReturn(1); + when(file.charset()).thenReturn(StandardCharsets.UTF_8); + + sourceHashHolder = new SourceHashHolder(file, lastSnapshots); + } + + @Test + public void should_lazy_load_line_hashes() throws Exception { + final String source = "source"; + FileUtils.write(ioFile, source + "\n", StandardCharsets.UTF_8); + when(file.lines()).thenReturn(2); + + assertThat(sourceHashHolder.getHashedSource().getHash(1)).isEqualTo(md5Hex(source)); + assertThat(sourceHashHolder.getHashedSource().getHash(2)).isEqualTo(""); + verify(file).key(); + verify(file).status(); + + assertThat(sourceHashHolder.getHashedSource().getHash(1)).isEqualTo(md5Hex(source)); + } + + @Test + public void should_lazy_load_reference_hashes_when_status_changed() throws Exception { + final String source = "source"; + String key = "foo:src/Foo.java"; + FileUtils.write(ioFile, source, StandardCharsets.UTF_8); + when(file.key()).thenReturn(key); + when(file.status()).thenReturn(InputFile.Status.CHANGED); + when(lastSnapshots.getLineHashes(key, null)).thenReturn(new String[] {md5Hex(source)}); + + assertThat(sourceHashHolder.getHashedReference().getHash(1)).isEqualTo(md5Hex(source)); + verify(lastSnapshots).getLineHashes(key, null); + + assertThat(sourceHashHolder.getHashedReference().getHash(1)).isEqualTo(md5Hex(source)); + Mockito.verifyNoMoreInteractions(lastSnapshots); + } + + @Test + public void should_not_load_reference_hashes_when_status_same() throws Exception { + final String source = "source"; + String key = "foo:src/Foo.java"; + FileUtils.write(ioFile, source, StandardCharsets.UTF_8); + when(file.key()).thenReturn(key); + when(file.status()).thenReturn(InputFile.Status.SAME); + + assertThat(sourceHashHolder.getHashedReference().getHash(1)).isEqualTo(md5Hex(source)); + Mockito.verifyNoMoreInteractions(lastSnapshots); + } + + @Test + public void no_reference_hashes_when_status_added() throws Exception { + final String source = "source"; + String key = "foo:src/Foo.java"; + FileUtils.write(ioFile, source, StandardCharsets.UTF_8); + when(file.key()).thenReturn(key); + when(file.status()).thenReturn(InputFile.Status.ADDED); + + assertThat(sourceHashHolder.getHashedReference()).isNull(); + Mockito.verifyNoMoreInteractions(lastSnapshots); + } + +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/tracking/TrackedIssueTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/tracking/TrackedIssueTest.java new file mode 100644 index 00000000000..4228f912b94 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/tracking/TrackedIssueTest.java @@ -0,0 +1,47 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.issue.tracking; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.Test; + +public class TrackedIssueTest { + @Test + public void round_trip() { + TrackedIssue issue = new TrackedIssue(); + issue.setStartLine(15); + + assertThat(issue.getLine()).isEqualTo(15); + assertThat(issue.startLine()).isEqualTo(15); + } + + @Test + public void hashes() { + String[] hashArr = new String[] { + "hash1", "hash2", "hash3" + }; + + FileHashes hashes = FileHashes.create(hashArr); + TrackedIssue issue = new TrackedIssue(hashes); + issue.setStartLine(1); + assertThat(issue.getLineHash()).isEqualTo("hash1"); + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/BatchMediumTester.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/BatchMediumTester.java new file mode 100644 index 00000000000..619977ce156 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/BatchMediumTester.java @@ -0,0 +1,497 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.mediumtest; + +import org.sonar.api.rule.RuleKey; + +import org.sonar.batch.rule.LoadedActiveRule; +import org.sonar.batch.repository.FileData; +import org.sonar.api.utils.DateUtils; +import com.google.common.collect.Table; +import com.google.common.collect.HashBasedTable; +import org.sonar.batch.repository.ProjectRepositories; +import org.sonar.batch.rule.ActiveRulesLoader; +import org.sonarqube.ws.QualityProfiles.SearchWsResponse.QualityProfile; +import org.sonar.batch.repository.QualityProfileLoader; +import org.apache.commons.io.FileUtils; +import org.apache.commons.lang.mutable.MutableBoolean; + +import javax.annotation.Nullable; + +import org.sonarqube.ws.Rules.ListResponse.Rule; +import org.sonar.batch.bootstrapper.IssueListener; +import org.sonar.api.server.rule.RulesDefinition.Repository; +import org.sonar.api.server.rule.RulesDefinition; +import org.sonar.batch.rule.RulesLoader; +import org.sonar.scanner.protocol.input.GlobalRepositories; +import org.sonar.scanner.protocol.input.ScannerInput.ServerIssue; +import com.google.common.base.Function; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.Reader; +import java.nio.charset.StandardCharsets; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +import org.sonar.api.CoreProperties; +import org.sonar.api.SonarPlugin; +import org.sonar.api.batch.debt.internal.DefaultDebtModel; +import org.sonar.api.measures.CoreMetrics; +import org.sonar.api.measures.Metric; +import org.sonar.batch.bootstrapper.Batch; +import org.sonar.batch.bootstrapper.EnvironmentInformation; +import org.sonar.batch.bootstrapper.LogOutput; +import org.sonar.batch.issue.tracking.ServerLineHashesLoader; +import org.sonar.batch.report.ReportPublisher; +import org.sonar.batch.repository.GlobalRepositoriesLoader; +import org.sonar.batch.repository.ProjectRepositoriesLoader; +import org.sonar.batch.repository.ServerIssuesLoader; + +/** + * Main utility class for writing batch medium tests. + * + */ +public class BatchMediumTester { + + public static final String MEDIUM_TEST_ENABLED = "sonar.mediumTest.enabled"; + private Batch batch; + private static Path workingDir = null; + private static Path globalWorkingDir = null; + + private static void createWorkingDirs() throws IOException { + destroyWorkingDirs(); + + workingDir = java.nio.file.Files.createTempDirectory("mediumtest-working-dir"); + globalWorkingDir = java.nio.file.Files.createTempDirectory("mediumtest-global-working-dir"); + } + + private static void destroyWorkingDirs() throws IOException { + if (workingDir != null) { + FileUtils.deleteDirectory(workingDir.toFile()); + workingDir = null; + } + + if (globalWorkingDir != null) { + FileUtils.deleteDirectory(globalWorkingDir.toFile()); + globalWorkingDir = null; + } + + } + + public static BatchMediumTesterBuilder builder() { + try { + createWorkingDirs(); + } catch (IOException e) { + e.printStackTrace(); + } + + BatchMediumTesterBuilder builder = new BatchMediumTesterBuilder().registerCoreMetrics(); + builder.bootstrapProperties.put(MEDIUM_TEST_ENABLED, "true"); + builder.bootstrapProperties.put(ReportPublisher.KEEP_REPORT_PROP_KEY, "true"); + builder.bootstrapProperties.put(CoreProperties.WORKING_DIRECTORY, workingDir.toString()); + builder.bootstrapProperties.put("sonar.userHome", globalWorkingDir.toString()); + return builder; + } + + public static class BatchMediumTesterBuilder { + private final FakeGlobalRepositoriesLoader globalRefProvider = new FakeGlobalRepositoriesLoader(); + private final FakeProjectRepositoriesLoader projectRefProvider = new FakeProjectRepositoriesLoader(); + private final FakePluginInstaller pluginInstaller = new FakePluginInstaller(); + private final FakeServerIssuesLoader serverIssues = new FakeServerIssuesLoader(); + private final FakeServerLineHashesLoader serverLineHashes = new FakeServerLineHashesLoader(); + private final Map<String, String> bootstrapProperties = new HashMap<>(); + private final FakeRulesLoader rulesLoader = new FakeRulesLoader(); + private final FakeQualityProfileLoader qualityProfiles = new FakeQualityProfileLoader(); + private final FakeActiveRulesLoader activeRules = new FakeActiveRulesLoader(); + private boolean associated = true; + private LogOutput logOutput = null; + + public BatchMediumTester build() { + return new BatchMediumTester(this); + } + + public BatchMediumTesterBuilder setAssociated(boolean associated) { + this.associated = associated; + return this; + } + + public BatchMediumTesterBuilder setLogOutput(LogOutput logOutput) { + this.logOutput = logOutput; + return this; + } + + public BatchMediumTesterBuilder registerPlugin(String pluginKey, File location) { + pluginInstaller.add(pluginKey, location); + return this; + } + + public BatchMediumTesterBuilder registerPlugin(String pluginKey, SonarPlugin instance) { + pluginInstaller.add(pluginKey, instance); + return this; + } + + public BatchMediumTesterBuilder registerCoreMetrics() { + for (Metric<?> m : CoreMetrics.getMetrics()) { + registerMetric(m); + } + return this; + } + + public BatchMediumTesterBuilder registerMetric(Metric<?> metric) { + globalRefProvider.add(metric); + return this; + } + + public BatchMediumTesterBuilder addQProfile(String language, String name) { + qualityProfiles.add(language, name); + return this; + } + + public BatchMediumTesterBuilder addRule(Rule rule) { + rulesLoader.addRule(rule); + return this; + } + + public BatchMediumTesterBuilder addRule(String key, String repoKey, String internalKey, String name) { + Rule.Builder builder = Rule.newBuilder(); + builder.setKey(key); + builder.setRepository(repoKey); + if (internalKey != null) { + builder.setInternalKey(internalKey); + } + builder.setName(name); + + rulesLoader.addRule(builder.build()); + return this; + } + + public BatchMediumTesterBuilder addRules(RulesDefinition rulesDefinition) { + RulesDefinition.Context context = new RulesDefinition.Context(); + rulesDefinition.define(context); + List<Repository> repositories = context.repositories(); + for (Repository repo : repositories) { + for (RulesDefinition.Rule rule : repo.rules()) { + this.addRule(rule.key(), rule.repository().key(), rule.internalKey(), rule.name()); + } + } + return this; + } + + public BatchMediumTesterBuilder addDefaultQProfile(String language, String name) { + addQProfile(language, name); + globalRefProvider.globalSettings().put("sonar.profile." + language, name); + return this; + } + + public BatchMediumTesterBuilder setPreviousAnalysisDate(Date previousAnalysis) { + projectRefProvider.setLastAnalysisDate(previousAnalysis); + return this; + } + + public BatchMediumTesterBuilder bootstrapProperties(Map<String, String> props) { + bootstrapProperties.putAll(props); + return this; + } + + public BatchMediumTesterBuilder activateRule(LoadedActiveRule activeRule) { + activeRules.addActiveRule(activeRule); + return this; + } + + public BatchMediumTesterBuilder addActiveRule(String repositoryKey, String ruleKey, @Nullable String templateRuleKey, String name, @Nullable String severity, + @Nullable String internalKey, @Nullable String languag) { + LoadedActiveRule r = new LoadedActiveRule(); + + r.setInternalKey(internalKey); + r.setRuleKey(RuleKey.of(repositoryKey, ruleKey)); + r.setName(name); + r.setTemplateRuleKey(templateRuleKey); + r.setLanguage(languag); + r.setSeverity(severity); + + activeRules.addActiveRule(r); + return this; + } + + public BatchMediumTesterBuilder addFileData(String moduleKey, String path, FileData fileData) { + projectRefProvider.addFileData(moduleKey, path, fileData); + return this; + } + + public BatchMediumTesterBuilder setLastBuildDate(Date d) { + projectRefProvider.setLastAnalysisDate(d); + return this; + } + + public BatchMediumTesterBuilder mockServerIssue(ServerIssue issue) { + serverIssues.getServerIssues().add(issue); + return this; + } + + public BatchMediumTesterBuilder mockLineHashes(String fileKey, String[] lineHashes) { + serverLineHashes.byKey.put(fileKey, lineHashes); + return this; + } + + } + + public void start() { + batch.start(); + } + + public void stop() { + batch.stop(); + try { + destroyWorkingDirs(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + public void syncProject(String projectKey) { + batch.syncProject(projectKey); + } + + private BatchMediumTester(BatchMediumTesterBuilder builder) { + Batch.Builder batchBuilder = Batch.builder() + .setEnableLoggingConfiguration(true) + .addComponents( + new EnvironmentInformation("mediumTest", "1.0"), + builder.pluginInstaller, + builder.globalRefProvider, + builder.qualityProfiles, + builder.rulesLoader, + builder.projectRefProvider, + builder.activeRules, + new DefaultDebtModel()) + .setBootstrapProperties(builder.bootstrapProperties) + .setLogOutput(builder.logOutput); + + if (builder.associated) { + batchBuilder.addComponents( + builder.serverIssues); + } + batch = batchBuilder.build(); + } + + public TaskBuilder newTask() { + return new TaskBuilder(this); + } + + public TaskBuilder newScanTask(File sonarProps) { + Properties prop = new Properties(); + try (Reader reader = new InputStreamReader(new FileInputStream(sonarProps), StandardCharsets.UTF_8)) { + prop.load(reader); + } catch (Exception e) { + throw new IllegalStateException("Unable to read configuration file", e); + } + TaskBuilder builder = new TaskBuilder(this); + builder.property("sonar.projectBaseDir", sonarProps.getParentFile().getAbsolutePath()); + for (Map.Entry<Object, Object> entry : prop.entrySet()) { + builder.property(entry.getKey().toString(), entry.getValue().toString()); + } + return builder; + } + + public static class TaskBuilder { + private final Map<String, String> taskProperties = new HashMap<>(); + private BatchMediumTester tester; + private IssueListener issueListener = null; + + public TaskBuilder(BatchMediumTester tester) { + this.tester = tester; + } + + public TaskResult start() { + TaskResult result = new TaskResult(); + Map<String, String> props = new HashMap<>(); + props.putAll(taskProperties); + if (issueListener != null) { + tester.batch.executeTask(props, result, issueListener); + } else { + tester.batch.executeTask(props, result); + } + return result; + } + + public TaskBuilder properties(Map<String, String> props) { + taskProperties.putAll(props); + return this; + } + + public TaskBuilder property(String key, String value) { + taskProperties.put(key, value); + return this; + } + + public TaskBuilder setIssueListener(IssueListener issueListener) { + this.issueListener = issueListener; + return this; + } + } + + private static class FakeRulesLoader implements RulesLoader { + private List<org.sonarqube.ws.Rules.ListResponse.Rule> rules = new LinkedList<>(); + + public FakeRulesLoader addRule(Rule rule) { + rules.add(rule); + return this; + } + + @Override + public List<Rule> load(@Nullable MutableBoolean fromCache) { + return rules; + } + } + + private static class FakeActiveRulesLoader implements ActiveRulesLoader { + private List<LoadedActiveRule> activeRules = new LinkedList<>(); + + public void addActiveRule(LoadedActiveRule activeRule) { + this.activeRules.add(activeRule); + } + + @Override + public List<LoadedActiveRule> load(String qualityProfileKey, MutableBoolean fromCache) { + return activeRules; + } + } + + private static class FakeGlobalRepositoriesLoader implements GlobalRepositoriesLoader { + + private int metricId = 1; + + private GlobalRepositories ref = new GlobalRepositories(); + + @Override + public GlobalRepositories load(@Nullable MutableBoolean fromCache) { + return ref; + } + + public Map<String, String> globalSettings() { + return ref.globalSettings(); + } + + public FakeGlobalRepositoriesLoader add(Metric<?> metric) { + Boolean optimizedBestValue = metric.isOptimizedBestValue(); + ref.metrics().add(new org.sonar.scanner.protocol.input.Metric(metricId, + metric.key(), + metric.getType().name(), + metric.getDescription(), + metric.getDirection(), + metric.getName(), + metric.getQualitative(), + metric.getUserManaged(), + metric.getWorstValue(), + metric.getBestValue(), + optimizedBestValue != null ? optimizedBestValue : false)); + metricId++; + return this; + } + + } + + private static class FakeProjectRepositoriesLoader implements ProjectRepositoriesLoader { + + private Table<String, String, FileData> fileDataTable = HashBasedTable.create(); + private Date lastAnalysisDate; + + @Override + public ProjectRepositories load(String projectKey, boolean isIssuesMode, @Nullable MutableBoolean fromCache) { + Table<String, String, String> settings = HashBasedTable.create(); + return new ProjectRepositories(settings, fileDataTable, lastAnalysisDate); + } + + public FakeProjectRepositoriesLoader addFileData(String moduleKey, String path, FileData fileData) { + fileDataTable.put(moduleKey, path, fileData); + return this; + } + + public FakeProjectRepositoriesLoader setLastAnalysisDate(Date d) { + lastAnalysisDate = d; + return this; + } + + } + + private static class FakeQualityProfileLoader implements QualityProfileLoader { + + private List<QualityProfile> qualityProfiles = new LinkedList<>(); + + public void add(String language, String name) { + qualityProfiles.add(QualityProfile.newBuilder() + .setLanguage(language) + .setKey(name) + .setName(name) + .setRulesUpdatedAt(DateUtils.formatDateTime(new Date(1234567891212L))) + .build()); + } + + @Override + public List<QualityProfile> load(String projectKey, String profileName, MutableBoolean fromCache) { + return qualityProfiles; + } + + @Override + public List<QualityProfile> loadDefault(String profileName, MutableBoolean fromCache) { + return qualityProfiles; + } + } + + private static class FakeServerIssuesLoader implements ServerIssuesLoader { + + private List<ServerIssue> serverIssues = new ArrayList<>(); + + public List<ServerIssue> getServerIssues() { + return serverIssues; + } + + @Override + public boolean load(String componentKey, Function<ServerIssue, Void> consumer) { + for (ServerIssue serverIssue : serverIssues) { + consumer.apply(serverIssue); + } + return true; + } + } + + private static class FakeServerLineHashesLoader implements ServerLineHashesLoader { + private Map<String, String[]> byKey = new HashMap<>(); + + @Override + public String[] getLineHashes(String fileKey, @Nullable MutableBoolean fromCache) { + if (byKey.containsKey(fileKey)) { + return byKey.get(fileKey); + } else { + throw new IllegalStateException("You forgot to mock line hashes for " + fileKey); + } + } + } + +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/LogOutputRecorder.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/LogOutputRecorder.java new file mode 100644 index 00000000000..edae5e47fb1 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/LogOutputRecorder.java @@ -0,0 +1,54 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.mediumtest; + +import java.util.Collection; +import java.util.LinkedList; +import java.util.List; + +import com.google.common.collect.Multimap; +import com.google.common.collect.HashMultimap; +import org.sonar.batch.bootstrapper.LogOutput; + +public class LogOutputRecorder implements LogOutput { + private Multimap<String, String> recordedByLevel = HashMultimap.create(); + private List<String> recorded = new LinkedList<>(); + private StringBuffer asString = new StringBuffer(); + + @Override + public void log(String formattedMessage, Level level) { + recordedByLevel.put(level.toString(), formattedMessage); + recorded.add(formattedMessage); + asString.append(formattedMessage).append("\n"); + } + + public Collection<String> getAll() { + return recorded; + } + + public Collection<String> get(String level) { + return recordedByLevel.get(level); + } + + public String getAsString() { + return asString.toString(); + } + +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/cache/CacheSyncTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/cache/CacheSyncTest.java new file mode 100644 index 00000000000..4ebf5eb1fa6 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/cache/CacheSyncTest.java @@ -0,0 +1,155 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.mediumtest.cache; + +import org.junit.rules.TemporaryFolder; + +import org.sonar.batch.mediumtest.TaskResult; +import org.sonar.batch.mediumtest.BatchMediumTester.TaskBuilder; +import org.sonar.batch.mediumtest.LogOutputRecorder; +import org.sonar.batch.repository.FileData; +import com.google.common.collect.ImmutableMap; + +import java.util.Date; + +import static org.assertj.core.api.Assertions.assertThat; +import org.junit.After; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.sonar.api.CoreProperties; +import org.sonar.batch.mediumtest.BatchMediumTester; +import org.sonar.xoo.XooPlugin; +import org.sonar.xoo.rule.XooRulesDefinition; + +public class CacheSyncTest { + @Rule + public ExpectedException exception = ExpectedException.none(); + + @Rule + public TemporaryFolder temp = new TemporaryFolder(); + + private BatchMediumTester tester; + + @After + public void stop() { + if (tester != null) { + tester.stop(); + tester = null; + } + } + + @Test + public void testExecuteTask() { + LogOutputRecorder logOutput = new LogOutputRecorder(); + + tester = BatchMediumTester.builder() + .bootstrapProperties(ImmutableMap.of(CoreProperties.ANALYSIS_MODE, CoreProperties.ANALYSIS_MODE_ISSUES, + "sonar.verbose", "true")) + .registerPlugin("xoo", new XooPlugin()) + .addRules(new XooRulesDefinition()) + .addQProfile("lang", "name") + .addActiveRule("xoo", "OneIssuePerLine", null, "One issue per line", "MAJOR", "my/internal/key", "xoo") + .setPreviousAnalysisDate(new Date()) + .addFileData("test-project", "file1", new FileData("hash", "123456789")) + .setLogOutput(logOutput) + .build(); + + tester.start(); + executeTask(tester.newTask()); + assertThat(logOutput.getAsString()).contains("Cache for project [key] not found, synchronizing"); + } + + @Test + public void testSyncFirstTime() { + LogOutputRecorder logOutput = new LogOutputRecorder(); + + tester = BatchMediumTester.builder() + .bootstrapProperties(ImmutableMap.of(CoreProperties.ANALYSIS_MODE, CoreProperties.ANALYSIS_MODE_ISSUES, + "sonar.verbose", "true")) + .registerPlugin("xoo", new XooPlugin()) + .addRules(new XooRulesDefinition()) + .addQProfile("lang", "name") + .addActiveRule("xoo", "OneIssuePerLine", null, "One issue per line", "MAJOR", "my/internal/key", "xoo") + .setPreviousAnalysisDate(new Date()) + .addFileData("test-project", "file1", new FileData("hash", "123456789")) + .setLogOutput(logOutput) + .build(); + + tester.start(); + tester.syncProject("test-project"); + assertThat(logOutput.getAsString()).contains("Cache for project [test-project] not found"); + } + + @Test + public void testSyncTwice() { + LogOutputRecorder logOutput = new LogOutputRecorder(); + + tester = BatchMediumTester.builder() + .bootstrapProperties(ImmutableMap.of(CoreProperties.ANALYSIS_MODE, CoreProperties.ANALYSIS_MODE_ISSUES, + "sonar.verbose", "true")) + .registerPlugin("xoo", new XooPlugin()) + .addRules(new XooRulesDefinition()) + .addQProfile("lang", "name") + .addActiveRule("xoo", "OneIssuePerLine", null, "One issue per line", "MAJOR", "my/internal/key", "xoo") + .setPreviousAnalysisDate(new Date()) + .addFileData("test-project", "file1", new FileData("hash", "123456789")) + .setLogOutput(logOutput) + .build(); + + tester.start(); + tester.syncProject("test-project"); + tester.syncProject("test-project"); + assertThat(logOutput.getAsString()).contains("-- Found project [test-project]"); + assertThat(logOutput.getAsString()).contains("not found, synchronizing data"); + assertThat(logOutput.getAsString()).contains("], synchronizing data (forced).."); + } + + @Test + public void testNonAssociated() { + LogOutputRecorder logOutput = new LogOutputRecorder(); + + tester = BatchMediumTester.builder() + .bootstrapProperties(ImmutableMap.of(CoreProperties.ANALYSIS_MODE, CoreProperties.ANALYSIS_MODE_ISSUES)) + .registerPlugin("xoo", new XooPlugin()) + .addRules(new XooRulesDefinition()) + .addQProfile("lang", "name") + .addActiveRule("xoo", "OneIssuePerLine", null, "One issue per line", "MAJOR", "my/internal/key", "xoo") + .setPreviousAnalysisDate(new Date()) + .addFileData("test-project", "file1", new FileData("hash", "123456789")) + .setLogOutput(logOutput) + .build(); + + tester.start(); + tester.syncProject(null); + + assertThat(logOutput.getAsString()).contains("Cache not found, synchronizing data"); + } + + private TaskResult executeTask(TaskBuilder builder) { + builder.property("sonar.projectKey", "key"); + builder.property("sonar.projectVersion", "1.0"); + builder.property("sonar.projectName", "key"); + builder.property("sonar.projectBaseDir", temp.getRoot().getAbsolutePath()); + builder.property("sonar.sources", temp.getRoot().getAbsolutePath()); + return builder.start(); + } + +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/coverage/CoverageMediumTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/coverage/CoverageMediumTest.java new file mode 100644 index 00000000000..e3dac6ed869 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/coverage/CoverageMediumTest.java @@ -0,0 +1,180 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.mediumtest.coverage; + +import com.google.common.collect.ImmutableMap; +import java.io.File; +import java.io.IOException; +import java.util.List; +import java.util.Map; +import org.apache.commons.io.FileUtils; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.measures.CoreMetrics; +import org.sonar.batch.mediumtest.BatchMediumTester; +import org.sonar.batch.mediumtest.TaskResult; +import org.sonar.xoo.XooPlugin; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.tuple; + +public class CoverageMediumTest { + + @org.junit.Rule + public TemporaryFolder temp = new TemporaryFolder(); + + public BatchMediumTester tester = BatchMediumTester.builder() + .registerPlugin("xoo", new XooPlugin()) + .addDefaultQProfile("xoo", "Sonar Way") + .build(); + + @Before + public void prepare() { + tester.start(); + } + + @After + public void stop() { + tester.stop(); + } + + @Test + public void unitTests() throws IOException { + + File baseDir = temp.getRoot(); + File srcDir = new File(baseDir, "src"); + srcDir.mkdir(); + + File xooFile = new File(srcDir, "sample.xoo"); + File xooUtCoverageFile = new File(srcDir, "sample.xoo.coverage"); + FileUtils.write(xooFile, "function foo() {\n if (a && b) {\nalert('hello');\n}\n}"); + FileUtils.write(xooUtCoverageFile, "2:2:2:1\n3:1"); + + TaskResult result = tester.newTask() + .properties(ImmutableMap.<String, String>builder() + .put("sonar.task", "scan") + .put("sonar.projectBaseDir", baseDir.getAbsolutePath()) + .put("sonar.projectKey", "com.foo.project") + .put("sonar.projectName", "Foo Project") + .put("sonar.projectVersion", "1.0-SNAPSHOT") + .put("sonar.projectDescription", "Description of Foo Project") + .put("sonar.sources", "src") + .build()) + .start(); + + InputFile file = result.inputFile("src/sample.xoo"); + assertThat(result.coverageFor(file, 2).getUtHits()).isTrue(); + assertThat(result.coverageFor(file, 2).getItHits()).isFalse(); + assertThat(result.coverageFor(file, 2).getConditions()).isEqualTo(2); + assertThat(result.coverageFor(file, 2).getUtCoveredConditions()).isEqualTo(1); + assertThat(result.coverageFor(file, 2).getItCoveredConditions()).isEqualTo(0); + assertThat(result.coverageFor(file, 2).getOverallCoveredConditions()).isEqualTo(0); + + Map<String, List<org.sonar.scanner.protocol.output.ScannerReport.Measure>> allMeasures = result.allMeasures(); + assertThat(allMeasures.get("com.foo.project:src/sample.xoo")).extracting("metricKey", "intValue") + .contains(tuple(CoreMetrics.LINES_TO_COVER_KEY, 2), + tuple(CoreMetrics.UNCOVERED_LINES_KEY, 0), + tuple(CoreMetrics.CONDITIONS_TO_COVER_KEY, 2), + tuple(CoreMetrics.UNCOVERED_CONDITIONS_KEY, 1)); + } + + @Test + public void exclusions() throws IOException { + + File baseDir = temp.getRoot(); + File srcDir = new File(baseDir, "src"); + srcDir.mkdir(); + + File xooFile = new File(srcDir, "sample.xoo"); + File xooUtCoverageFile = new File(srcDir, "sample.xoo.coverage"); + FileUtils.write(xooFile, "function foo() {\n if (a && b) {\nalert('hello');\n}\n}"); + FileUtils.write(xooUtCoverageFile, "2:2:2:1\n3:1"); + + TaskResult result = tester.newTask() + .properties(ImmutableMap.<String, String>builder() + .put("sonar.task", "scan") + .put("sonar.projectBaseDir", baseDir.getAbsolutePath()) + .put("sonar.projectKey", "com.foo.project") + .put("sonar.projectName", "Foo Project") + .put("sonar.projectVersion", "1.0-SNAPSHOT") + .put("sonar.projectDescription", "Description of Foo Project") + .put("sonar.sources", "src") + .put("sonar.coverage.exclusions", "**/sample.xoo") + .build()) + .start(); + + InputFile file = result.inputFile("src/sample.xoo"); + assertThat(result.coverageFor(file, 2)).isNull(); + + Map<String, List<org.sonar.scanner.protocol.output.ScannerReport.Measure>> allMeasures = result.allMeasures(); + assertThat(allMeasures.get("com.foo.project:src/sample.xoo")).extracting("metricKey") + .doesNotContain(CoreMetrics.LINES_TO_COVER_KEY, CoreMetrics.UNCOVERED_LINES_KEY, CoreMetrics.CONDITIONS_TO_COVER_KEY, + CoreMetrics.COVERED_CONDITIONS_BY_LINE_KEY); + } + + @Test + public void fallbackOnExecutableLines() throws IOException { + + File baseDir = temp.getRoot(); + File srcDir = new File(baseDir, "src"); + srcDir.mkdir(); + + File xooFile = new File(srcDir, "sample.xoo"); + File measuresFile = new File(srcDir, "sample.xoo.measures"); + FileUtils.write(xooFile, "function foo() {\n if (a && b) {\nalert('hello');\n}\n}"); + FileUtils.write(measuresFile, "executable_lines_data:2=1;3=1;4=0"); + + TaskResult result = tester.newTask() + .properties(ImmutableMap.<String, String>builder() + .put("sonar.task", "scan") + .put("sonar.projectBaseDir", baseDir.getAbsolutePath()) + .put("sonar.projectKey", "com.foo.project") + .put("sonar.projectName", "Foo Project") + .put("sonar.projectVersion", "1.0-SNAPSHOT") + .put("sonar.projectDescription", "Description of Foo Project") + .put("sonar.sources", "src") + .build()) + .start(); + + InputFile file = result.inputFile("src/sample.xoo"); + assertThat(result.coverageFor(file, 1)).isNull(); + + assertThat(result.coverageFor(file, 2).getUtHits()).isFalse(); + assertThat(result.coverageFor(file, 2).getItHits()).isFalse(); + assertThat(result.coverageFor(file, 2).getConditions()).isEqualTo(0); + assertThat(result.coverageFor(file, 2).getUtCoveredConditions()).isEqualTo(0); + assertThat(result.coverageFor(file, 2).getItCoveredConditions()).isEqualTo(0); + assertThat(result.coverageFor(file, 2).getOverallCoveredConditions()).isEqualTo(0); + + assertThat(result.coverageFor(file, 3).getUtHits()).isFalse(); + assertThat(result.coverageFor(file, 4)).isNull(); + + Map<String, List<org.sonar.scanner.protocol.output.ScannerReport.Measure>> allMeasures = result.allMeasures(); + assertThat(allMeasures.get("com.foo.project:src/sample.xoo")).extracting("metricKey", "intValue") + .contains(tuple(CoreMetrics.LINES_TO_COVER_KEY, 2), + tuple(CoreMetrics.UNCOVERED_LINES_KEY, 2)); + + assertThat(allMeasures.get("com.foo.project:src/sample.xoo")).extracting("metricKey").doesNotContain(CoreMetrics.CONDITIONS_TO_COVER_KEY, CoreMetrics.UNCOVERED_CONDITIONS_KEY); + } + +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/cpd/CpdMediumTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/cpd/CpdMediumTest.java new file mode 100644 index 00000000000..662ed7dbcfd --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/cpd/CpdMediumTest.java @@ -0,0 +1,336 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.mediumtest.cpd; + +import com.google.common.collect.ImmutableMap; +import java.io.File; +import java.io.IOException; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import org.apache.commons.io.FileUtils; +import org.assertj.core.groups.Tuple; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.rules.TemporaryFolder; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.measures.CoreMetrics; +import org.sonar.batch.mediumtest.BatchMediumTester; +import org.sonar.batch.mediumtest.TaskResult; +import org.sonar.scanner.protocol.output.ScannerReport; +import org.sonar.scanner.protocol.output.ScannerReport.Measure; +import org.sonar.xoo.XooPlugin; +import org.sonar.xoo.lang.CpdTokenizerSensor; + +import static org.assertj.core.api.Assertions.assertThat; + +@RunWith(Parameterized.class) +public class CpdMediumTest { + + @Parameters(name = "new api: {0}") + public static Collection<Object[]> data() { + return Arrays.asList(new Object[][] { + {true}, {false} + }); + } + + @Rule + public TemporaryFolder temp = new TemporaryFolder(); + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + public BatchMediumTester tester = BatchMediumTester.builder() + .registerPlugin("xoo", new XooPlugin()) + .addDefaultQProfile("xoo", "Sonar Way") + .build(); + + private File baseDir; + + private ImmutableMap.Builder<String, String> builder; + + private boolean useNewSensorApi; + + public CpdMediumTest(boolean useNewSensorApi) { + this.useNewSensorApi = useNewSensorApi; + } + + @Before + public void prepare() throws IOException { + tester.start(); + + baseDir = temp.getRoot(); + + builder = ImmutableMap.<String, String>builder() + .put("sonar.task", "scan") + .put("sonar.projectBaseDir", baseDir.getAbsolutePath()) + .put("sonar.projectKey", "com.foo.project") + .put("sonar.projectName", "Foo Project") + .put("sonar.projectVersion", "1.0-SNAPSHOT") + .put("sonar.projectDescription", "Description of Foo Project"); + if (useNewSensorApi) { + builder.put(CpdTokenizerSensor.ENABLE_PROP, "true"); + } + } + + @After + public void stop() { + tester.stop(); + } + + @Test + public void testCrossModuleDuplications() throws IOException { + builder.put("sonar.modules", "module1,module2") + .put("sonar.cpd.xoo.minimumTokens", "10") + .put("sonar.verbose", "true"); + + // module 1 + builder.put("module1.sonar.projectKey", "module1"); + builder.put("module1.sonar.projectName", "Module 1"); + builder.put("module1.sonar.sources", "."); + + // module2 + builder.put("module2.sonar.projectKey", "module2"); + builder.put("module2.sonar.projectName", "Module 2"); + builder.put("module2.sonar.sources", "."); + + File module1Dir = new File(baseDir, "module1"); + File module2Dir = new File(baseDir, "module2"); + + module1Dir.mkdir(); + module2Dir.mkdir(); + + String duplicatedStuff = "Sample xoo\ncontent\n" + + "foo\nbar\ntoto\ntiti\n" + + "foo\nbar\ntoto\ntiti\n" + + "bar\ntoto\ntiti\n" + + "foo\nbar\ntoto\ntiti"; + + // create duplicated file in both modules + File xooFile1 = new File(module1Dir, "sample1.xoo"); + FileUtils.write(xooFile1, duplicatedStuff); + + File xooFile2 = new File(module2Dir, "sample2.xoo"); + FileUtils.write(xooFile2, duplicatedStuff); + + TaskResult result = tester.newTask().properties(builder.build()).start(); + + assertThat(result.inputFiles()).hasSize(2); + + InputFile inputFile1 = result.inputFile("sample1.xoo"); + InputFile inputFile2 = result.inputFile("sample2.xoo"); + + // One clone group on each file + List<org.sonar.scanner.protocol.output.ScannerReport.Duplication> duplicationGroupsFile1 = result.duplicationsFor(inputFile1); + assertThat(duplicationGroupsFile1).hasSize(1); + + org.sonar.scanner.protocol.output.ScannerReport.Duplication cloneGroupFile1 = duplicationGroupsFile1.get(0); + assertThat(cloneGroupFile1.getOriginPosition().getStartLine()).isEqualTo(1); + assertThat(cloneGroupFile1.getOriginPosition().getEndLine()).isEqualTo(17); + assertThat(cloneGroupFile1.getDuplicateList()).hasSize(1); + assertThat(cloneGroupFile1.getDuplicate(0).getOtherFileRef()).isEqualTo(result.getReportComponent(((DefaultInputFile) inputFile2).key()).getRef()); + + List<org.sonar.scanner.protocol.output.ScannerReport.Duplication> duplicationGroupsFile2 = result.duplicationsFor(inputFile2); + assertThat(duplicationGroupsFile2).hasSize(1); + + org.sonar.scanner.protocol.output.ScannerReport.Duplication cloneGroupFile2 = duplicationGroupsFile2.get(0); + assertThat(cloneGroupFile2.getOriginPosition().getStartLine()).isEqualTo(1); + assertThat(cloneGroupFile2.getOriginPosition().getEndLine()).isEqualTo(17); + assertThat(cloneGroupFile2.getDuplicateList()).hasSize(1); + assertThat(cloneGroupFile2.getDuplicate(0).getOtherFileRef()).isEqualTo(result.getReportComponent(((DefaultInputFile) inputFile1).key()).getRef()); + + assertThat(result.duplicationBlocksFor(inputFile1)).isEmpty(); + } + + @Test + public void testCrossFileDuplications() throws IOException { + File srcDir = new File(baseDir, "src"); + srcDir.mkdir(); + + String duplicatedStuff = "Sample xoo\ncontent\n" + + "foo\nbar\ntoto\ntiti\n" + + "foo\nbar\ntoto\ntiti\n" + + "bar\ntoto\ntiti\n" + + "foo\nbar\ntoto\ntiti"; + + File xooFile1 = new File(srcDir, "sample1.xoo"); + FileUtils.write(xooFile1, duplicatedStuff); + + File xooFile2 = new File(srcDir, "sample2.xoo"); + FileUtils.write(xooFile2, duplicatedStuff); + + TaskResult result = tester.newTask() + .properties(builder + .put("sonar.sources", "src") + .put("sonar.cpd.xoo.minimumTokens", "10") + .put("sonar.verbose", "true") + .build()) + .start(); + + assertThat(result.inputFiles()).hasSize(2); + + InputFile inputFile1 = result.inputFile("src/sample1.xoo"); + InputFile inputFile2 = result.inputFile("src/sample2.xoo"); + + // One clone group on each file + List<org.sonar.scanner.protocol.output.ScannerReport.Duplication> duplicationGroupsFile1 = result.duplicationsFor(inputFile1); + assertThat(duplicationGroupsFile1).hasSize(1); + + org.sonar.scanner.protocol.output.ScannerReport.Duplication cloneGroupFile1 = duplicationGroupsFile1.get(0); + assertThat(cloneGroupFile1.getOriginPosition().getStartLine()).isEqualTo(1); + assertThat(cloneGroupFile1.getOriginPosition().getEndLine()).isEqualTo(17); + assertThat(cloneGroupFile1.getDuplicateList()).hasSize(1); + assertThat(cloneGroupFile1.getDuplicate(0).getOtherFileRef()).isEqualTo(result.getReportComponent(((DefaultInputFile) inputFile2).key()).getRef()); + + List<org.sonar.scanner.protocol.output.ScannerReport.Duplication> duplicationGroupsFile2 = result.duplicationsFor(inputFile2); + assertThat(duplicationGroupsFile2).hasSize(1); + + org.sonar.scanner.protocol.output.ScannerReport.Duplication cloneGroupFile2 = duplicationGroupsFile2.get(0); + assertThat(cloneGroupFile2.getOriginPosition().getStartLine()).isEqualTo(1); + assertThat(cloneGroupFile2.getOriginPosition().getEndLine()).isEqualTo(17); + assertThat(cloneGroupFile2.getDuplicateList()).hasSize(1); + assertThat(cloneGroupFile2.getDuplicate(0).getOtherFileRef()).isEqualTo(result.getReportComponent(((DefaultInputFile) inputFile1).key()).getRef()); + + assertThat(result.duplicationBlocksFor(inputFile1)).isEmpty(); + } + + @Test + public void enableCrossProjectDuplication() throws IOException { + File srcDir = new File(baseDir, "src"); + srcDir.mkdir(); + + String duplicatedStuff = "Sample xoo\ncontent\nfoo\nbar\ntoto\ntiti\nfoo"; + + File xooFile1 = new File(srcDir, "sample1.xoo"); + FileUtils.write(xooFile1, duplicatedStuff); + + TaskResult result = tester.newTask() + .properties(builder + .put("sonar.sources", "src") + .put("sonar.cpd.xoo.minimumTokens", "1") + .put("sonar.cpd.xoo.minimumLines", "5") + .put("sonar.verbose", "true") + .put("sonar.cpd.cross_project", "true") + .build()) + .start(); + + InputFile inputFile1 = result.inputFile("src/sample1.xoo"); + + List<ScannerReport.CpdTextBlock> duplicationBlocks = result.duplicationBlocksFor(inputFile1); + assertThat(duplicationBlocks).hasSize(3); + assertThat(duplicationBlocks.get(0).getStartLine()).isEqualTo(1); + assertThat(duplicationBlocks.get(0).getEndLine()).isEqualTo(5); + assertThat(duplicationBlocks.get(0).getStartTokenIndex()).isEqualTo(1); + assertThat(duplicationBlocks.get(0).getEndTokenIndex()).isEqualTo(6); + assertThat(duplicationBlocks.get(0).getHash()).isNotEmpty(); + + assertThat(duplicationBlocks.get(1).getStartLine()).isEqualTo(2); + assertThat(duplicationBlocks.get(1).getEndLine()).isEqualTo(6); + assertThat(duplicationBlocks.get(1).getStartTokenIndex()).isEqualTo(3); + assertThat(duplicationBlocks.get(1).getEndTokenIndex()).isEqualTo(7); + assertThat(duplicationBlocks.get(0).getHash()).isNotEmpty(); + + assertThat(duplicationBlocks.get(2).getStartLine()).isEqualTo(3); + assertThat(duplicationBlocks.get(2).getEndLine()).isEqualTo(7); + assertThat(duplicationBlocks.get(2).getStartTokenIndex()).isEqualTo(4); + assertThat(duplicationBlocks.get(2).getEndTokenIndex()).isEqualTo(8); + assertThat(duplicationBlocks.get(0).getHash()).isNotEmpty(); + } + + // SONAR-6000 + @Test + public void truncateDuplication() throws IOException { + File srcDir = new File(baseDir, "src"); + srcDir.mkdir(); + + String duplicatedStuff = "Sample xoo\n"; + + int blockCount = 10000; + File xooFile1 = new File(srcDir, "sample.xoo"); + for (int i = 0; i < blockCount; i++) { + FileUtils.write(xooFile1, duplicatedStuff, true); + FileUtils.write(xooFile1, "" + i + "\n", true); + } + + TaskResult result = tester.newTask() + .properties(builder + .put("sonar.sources", "src") + .put("sonar.cpd.xoo.minimumTokens", "1") + .put("sonar.cpd.xoo.minimumLines", "1") + .build()) + .start(); + + Map<String, List<Measure>> allMeasures = result.allMeasures(); + + assertThat(allMeasures.get("com.foo.project")).extracting("metricKey", "intValue", "doubleValue", "stringValue").containsOnly( + Tuple.tuple(CoreMetrics.QUALITY_PROFILES_KEY, 0, 0.0, + "[{\"key\":\"Sonar Way\",\"language\":\"xoo\",\"name\":\"Sonar Way\",\"rulesUpdatedAt\":\"2009-02-13T23:31:31+0000\"}]")); + + assertThat(allMeasures.get("com.foo.project:src/sample.xoo")).extracting("metricKey", "intValue").containsOnly( + Tuple.tuple(CoreMetrics.LINES_KEY, blockCount * 2 + 1)); + + List<org.sonar.scanner.protocol.output.ScannerReport.Duplication> duplicationGroups = result.duplicationsFor(result.inputFile("src/sample.xoo")); + assertThat(duplicationGroups).hasSize(1); + + org.sonar.scanner.protocol.output.ScannerReport.Duplication cloneGroup = duplicationGroups.get(0); + assertThat(cloneGroup.getDuplicateList()).hasSize(100); + } + + @Test + public void testIntraFileDuplications() throws IOException { + File srcDir = new File(baseDir, "src"); + srcDir.mkdir(); + + String content = "Sample xoo\ncontent\nfoo\nbar\nSample xoo\ncontent\n"; + + File xooFile = new File(srcDir, "sample.xoo"); + FileUtils.write(xooFile, content); + + TaskResult result = tester.newTask() + .properties(builder + .put("sonar.sources", "src") + .put("sonar.cpd.xoo.minimumTokens", "2") + .put("sonar.cpd.xoo.minimumLines", "2") + .put("sonar.verbose", "true") + .build()) + .start(); + + InputFile inputFile = result.inputFile("src/sample.xoo"); + // One clone group + List<org.sonar.scanner.protocol.output.ScannerReport.Duplication> duplicationGroups = result.duplicationsFor(inputFile); + assertThat(duplicationGroups).hasSize(1); + + org.sonar.scanner.protocol.output.ScannerReport.Duplication cloneGroup = duplicationGroups.get(0); + assertThat(cloneGroup.getOriginPosition().getStartLine()).isEqualTo(1); + assertThat(cloneGroup.getOriginPosition().getEndLine()).isEqualTo(2); + assertThat(cloneGroup.getDuplicateList()).hasSize(1); + assertThat(cloneGroup.getDuplicate(0).getRange().getStartLine()).isEqualTo(5); + assertThat(cloneGroup.getDuplicate(0).getRange().getEndLine()).isEqualTo(6); + } + +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/deprecated/DeprecatedApiMediumTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/deprecated/DeprecatedApiMediumTest.java new file mode 100644 index 00000000000..fa1ff38833d --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/deprecated/DeprecatedApiMediumTest.java @@ -0,0 +1,134 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.mediumtest.deprecated; + +import com.google.common.collect.ImmutableMap; +import java.io.File; +import java.io.IOException; +import org.apache.commons.io.FileUtils; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.sonar.batch.mediumtest.BatchMediumTester; +import org.sonar.batch.mediumtest.TaskResult; +import org.sonar.xoo.XooPlugin; +import org.sonar.xoo.rule.XooRulesDefinition; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.groups.Tuple.tuple; + +public class DeprecatedApiMediumTest { + + @org.junit.Rule + public TemporaryFolder temp = new TemporaryFolder(); + + public BatchMediumTester tester = BatchMediumTester.builder() + .registerPlugin("xoo", new XooPlugin()) + .addRules(new XooRulesDefinition()) + .addDefaultQProfile("xoo", "Sonar Way") + .addActiveRule("xoo", "DeprecatedResourceApi", null, "One issue per line", "MAJOR", null, "xoo") + .build(); + + @Before + public void prepare() { + tester.start(); + } + + @After + public void stop() { + tester.stop(); + } + + @Test + public void testIssueDetails() throws IOException { + + File baseDir = temp.getRoot(); + File srcDir = new File(baseDir, "src"); + srcDir.mkdir(); + + File xooFileInRootDir = new File(srcDir, "sample.xoo"); + FileUtils.write(xooFileInRootDir, "1\n2\n3\n4\n5\n6\n7\n8\n9\n10"); + + File xooFileInAnotherDir = new File(srcDir, "package/sample.xoo"); + FileUtils.write(xooFileInAnotherDir, "1\n2\n3\n4\n5\n6\n7\n8\n9\n10"); + + TaskResult result = tester.newTask() + .properties(ImmutableMap.<String, String>builder() + .put("sonar.task", "scan") + .put("sonar.projectBaseDir", baseDir.getAbsolutePath()) + .put("sonar.projectKey", "com.foo.project") + .put("sonar.projectName", "Foo Project") + .put("sonar.projectVersion", "1.0-SNAPSHOT") + .put("sonar.projectDescription", "Description of Foo Project") + .put("sonar.sources", "src") + .build()) + .start(); + + assertThat(result.issuesFor(result.inputFile("src/sample.xoo"))).extracting("msg", "line").containsOnly( + tuple("Issue created using deprecated API", 0), + tuple("Issue created using deprecated API", 1)); + assertThat(result.issuesFor(result.inputFile("src/package/sample.xoo"))).extracting("msg", "line").containsOnly( + tuple("Issue created using deprecated API", 0), + tuple("Issue created using deprecated API", 1)); + assertThat(result.issuesFor(result.inputDir("src"))).extracting("msg", "line").containsOnly( + tuple("Issue created using deprecated API", 0)); + assertThat(result.issuesFor(result.inputDir("src/package"))).extracting("msg", "line").containsOnly( + tuple("Issue created using deprecated API", 0)); + + } + + @Test + public void createIssueOnRootDir() throws IOException { + + File baseDir = temp.getRoot(); + + File xooFileInRootDir = new File(baseDir, "sample.xoo"); + FileUtils.write(xooFileInRootDir, "1\n2\n3\n4\n5\n6\n7\n8\n9\n10"); + + File xooFileInAnotherDir = new File(baseDir, "package/sample.xoo"); + FileUtils.write(xooFileInAnotherDir, "1\n2\n3\n4\n5\n6\n7\n8\n9\n10"); + + TaskResult result = tester.newTask() + .properties(ImmutableMap.<String, String>builder() + .put("sonar.task", "scan") + .put("sonar.projectBaseDir", baseDir.getAbsolutePath()) + .put("sonar.projectKey", "com.foo.project") + .put("sonar.projectName", "Foo Project") + .put("sonar.projectVersion", "1.0-SNAPSHOT") + .put("sonar.projectDescription", "Description of Foo Project") + .put("sonar.sources", ".") + .build()) + .start(); + + assertThat(result.issuesFor(result.inputFile("sample.xoo"))).extracting("msg", "line").containsOnly( + tuple("Issue created using deprecated API", 0), + tuple("Issue created using deprecated API", 1)); + assertThat(result.issuesFor(result.inputFile("package/sample.xoo"))).extracting("msg", "line").containsOnly( + tuple("Issue created using deprecated API", 0), + tuple("Issue created using deprecated API", 1)); + assertThat(result.issuesFor(result.inputDir(""))).extracting("msg", "line").containsOnly( + tuple("Issue created using deprecated API", 0)); + assertThat(result.issuesFor(result.inputDir("package"))).extracting("msg", "line").containsOnly( + tuple("Issue created using deprecated API", 0)); + + } + +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/fs/FileSystemMediumTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/fs/FileSystemMediumTest.java new file mode 100644 index 00000000000..dcb32c1030e --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/fs/FileSystemMediumTest.java @@ -0,0 +1,296 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.mediumtest.fs; + +import com.google.common.collect.ImmutableMap; +import org.apache.commons.io.FileUtils; +import org.apache.commons.lang.StringUtils; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.rules.TemporaryFolder; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.utils.MessageException; +import org.sonar.api.utils.System2; +import org.sonar.batch.mediumtest.BatchMediumTester; +import org.sonar.batch.mediumtest.TaskResult; +import org.sonar.xoo.XooPlugin; + +import java.io.File; +import java.io.IOException; + +import static org.assertj.core.api.Assertions.assertThat; + +public class FileSystemMediumTest { + + @Rule + public TemporaryFolder temp = new TemporaryFolder(); + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + public BatchMediumTester tester = BatchMediumTester.builder() + .registerPlugin("xoo", new XooPlugin()) + .addDefaultQProfile("xoo", "Sonar Way") + .build(); + + private File baseDir; + + private ImmutableMap.Builder<String, String> builder; + + @Before + public void prepare() throws IOException { + tester.start(); + + baseDir = temp.getRoot(); + + builder = ImmutableMap.<String, String>builder() + .put("sonar.task", "scan") + .put("sonar.projectBaseDir", baseDir.getAbsolutePath()) + .put("sonar.projectKey", "com.foo.project") + .put("sonar.projectName", "Foo Project") + .put("sonar.projectVersion", "1.0-SNAPSHOT") + .put("sonar.projectDescription", "Description of Foo Project"); + } + + @After + public void stop() { + tester.stop(); + } + + @Test + public void scanProjectWithSourceDir() throws IOException { + File srcDir = new File(baseDir, "src"); + srcDir.mkdir(); + + File xooFile = new File(srcDir, "sample.xoo"); + FileUtils.write(xooFile, "Sample xoo\ncontent"); + + TaskResult result = tester.newTask() + .properties(builder + .put("sonar.sources", "src") + .build()) + .start(); + + assertThat(result.inputFiles()).hasSize(1); + assertThat(result.inputDirs()).hasSize(1); + assertThat(result.inputFile("src/sample.xoo").type()).isEqualTo(InputFile.Type.MAIN); + assertThat(result.inputFile("src/sample.xoo").relativePath()).isEqualTo("src/sample.xoo"); + assertThat(result.inputDir("src").relativePath()).isEqualTo("src"); + } + + @Test + public void scanBigProject() throws IOException { + File srcDir = new File(baseDir, "src"); + srcDir.mkdir(); + + int nbFiles = 100; + int ruleCount = 100000; + for (int nb = 1; nb <= nbFiles; nb++) { + File xooFile = new File(srcDir, "sample" + nb + ".xoo"); + FileUtils.write(xooFile, StringUtils.repeat(StringUtils.repeat("a", 100) + "\n", ruleCount / 1000)); + } + + TaskResult result = tester.newTask() + .properties(builder + .put("sonar.sources", "src") + .build()) + .start(); + + assertThat(result.inputFiles()).hasSize(100); + assertThat(result.inputDirs()).hasSize(1); + } + + @Test + public void scanProjectWithTestDir() throws IOException { + File test = new File(baseDir, "test"); + test.mkdir(); + + File xooFile = new File(test, "sampleTest.xoo"); + FileUtils.write(xooFile, "Sample test xoo\ncontent"); + + TaskResult result = tester.newTask() + .properties(builder + .put("sonar.sources", "") + .put("sonar.tests", "test") + .build()) + .start(); + + assertThat(result.inputFiles()).hasSize(1); + assertThat(result.inputFile("test/sampleTest.xoo").type()).isEqualTo(InputFile.Type.TEST); + } + + /** + * SONAR-5419 + */ + @Test + public void scanProjectWithMixedSourcesAndTests() throws IOException { + File srcDir = new File(baseDir, "src"); + srcDir.mkdir(); + + File xooFile = new File(srcDir, "sample.xoo"); + FileUtils.write(xooFile, "Sample xoo\ncontent"); + + File xooFile2 = new File(baseDir, "another.xoo"); + FileUtils.write(xooFile2, "Sample xoo 2\ncontent"); + + File testDir = new File(baseDir, "test"); + testDir.mkdir(); + + File xooTestFile = new File(baseDir, "sampleTest2.xoo"); + FileUtils.write(xooTestFile, "Sample test xoo\ncontent"); + + File xooTestFile2 = new File(testDir, "sampleTest.xoo"); + FileUtils.write(xooTestFile2, "Sample test xoo 2\ncontent"); + + TaskResult result = tester.newTask() + .properties(builder + .put("sonar.sources", "src,another.xoo") + .put("sonar.tests", "test,sampleTest2.xoo") + .build()) + .start(); + + assertThat(result.inputFiles()).hasSize(4); + } + + @Test + public void fileInclusionsExclusions() throws IOException { + File srcDir = new File(baseDir, "src"); + srcDir.mkdir(); + + File xooFile = new File(srcDir, "sample.xoo"); + FileUtils.write(xooFile, "Sample xoo\ncontent"); + + File xooFile2 = new File(baseDir, "another.xoo"); + FileUtils.write(xooFile2, "Sample xoo 2\ncontent"); + + File testDir = new File(baseDir, "test"); + testDir.mkdir(); + + File xooTestFile = new File(baseDir, "sampleTest2.xoo"); + FileUtils.write(xooTestFile, "Sample test xoo\ncontent"); + + File xooTestFile2 = new File(testDir, "sampleTest.xoo"); + FileUtils.write(xooTestFile2, "Sample test xoo 2\ncontent"); + + TaskResult result = tester.newTask() + .properties(builder + .put("sonar.sources", "src,another.xoo") + .put("sonar.tests", "test,sampleTest2.xoo") + .put("sonar.inclusions", "src/**") + .put("sonar.exclusions", "**/another.*") + .put("sonar.test.inclusions", "**/sampleTest*.*") + .put("sonar.test.exclusions", "**/sampleTest2.xoo") + .build()) + .start(); + + assertThat(result.inputFiles()).hasSize(2); + } + + @Test + public void failForDuplicateInputFile() throws IOException { + File srcDir = new File(baseDir, "src"); + srcDir.mkdir(); + + File xooFile = new File(srcDir, "sample.xoo"); + FileUtils.write(xooFile, "Sample xoo\ncontent"); + + thrown.expect(MessageException.class); + thrown.expectMessage("can't be indexed twice. Please check that inclusion/exclusion patterns produce disjoint sets for main and test files"); + tester.newTask() + .properties(builder + .put("sonar.sources", "src,src/sample.xoo") + .build()) + .start(); + + } + + // SONAR-5330 + @Test + public void scanProjectWithSourceSymlink() { + if (!System2.INSTANCE.isOsWindows()) { + File projectDir = new File("src/test/resources/mediumtest/xoo/sample-with-symlink"); + TaskResult result = tester + .newScanTask(new File(projectDir, "sonar-project.properties")) + .start(); + + assertThat(result.inputFiles()).hasSize(3); + // check that symlink was not resolved to target + assertThat(result.inputFiles()).extractingResultOf("path").toString().startsWith(projectDir.toString()); + } + } + + // SONAR-6719 + @Test + public void scanProjectWithWrongCase() { + if (System2.INSTANCE.isOsWindows()) { + File projectDir = new File("src/test/resources/mediumtest/xoo/sample"); + TaskResult result = tester + .newScanTask(new File(projectDir, "sonar-project.properties")) + .property("sonar.sources", "XOURCES") + .property("sonar.tests", "TESTX") + .start(); + + assertThat(result.inputFiles()).hasSize(3); + assertThat(result.inputFiles()).extractingResultOf("relativePath").containsOnly( + "xources/hello/HelloJava.xoo", + "xources/hello/helloscala.xoo", + "testx/ClassOneTest.xoo"); + } + } + + @Test + public void indexAnyFile() throws IOException { + File srcDir = new File(baseDir, "src"); + srcDir.mkdir(); + + File xooFile = new File(srcDir, "sample.xoo"); + FileUtils.write(xooFile, "Sample xoo\ncontent"); + + File otherFile = new File(srcDir, "sample.other"); + FileUtils.write(otherFile, "Sample other\ncontent"); + + TaskResult result = tester.newTask() + .properties(builder + .put("sonar.sources", "src") + .put("sonar.import_unknown_files", "true") + .build()) + .start(); + + assertThat(result.inputFiles()).hasSize(2); + assertThat(result.inputFile("src/sample.other").type()).isEqualTo(InputFile.Type.MAIN); + assertThat(result.inputFile("src/sample.other").relativePath()).isEqualTo("src/sample.other"); + assertThat(result.inputFile("src/sample.other").language()).isNull(); + } + + @Test + public void scanMultiModuleProject() { + File projectDir = new File("src/test/resources/mediumtest/xoo/multi-modules-sample"); + TaskResult result = tester + .newScanTask(new File(projectDir, "sonar-project.properties")) + .start(); + + assertThat(result.inputFiles()).hasSize(4); + assertThat(result.inputDirs()).hasSize(4); + } + +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/fs/NoLanguagesPluginsMediumTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/fs/NoLanguagesPluginsMediumTest.java new file mode 100644 index 00000000000..90502c54ae8 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/fs/NoLanguagesPluginsMediumTest.java @@ -0,0 +1,76 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.mediumtest.fs; + +import org.junit.rules.ExpectedException; + +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.filefilter.FileFilterUtils; +import org.sonar.batch.mediumtest.issuesmode.IssueModeAndReportsMediumTest; + +import java.io.File; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.Rule; +import org.junit.rules.TemporaryFolder; +import org.sonar.batch.mediumtest.BatchMediumTester; + +public class NoLanguagesPluginsMediumTest { + @Rule + public TemporaryFolder temp = new TemporaryFolder(); + + @Rule + public ExpectedException exception = ExpectedException.none(); + + public BatchMediumTester tester = BatchMediumTester.builder() + .setPreviousAnalysisDate(null) + .build(); + + @Before + public void prepare() { + tester.start(); + } + + @After + public void stop() { + tester.stop(); + } + + @Test + public void testNoLanguagePluginsInstalled() throws Exception { + File projectDir = copyProject("/mediumtest/xoo/sample"); + + exception.expect(IllegalStateException.class); + exception.expectMessage("No language plugins are installed"); + + tester + .newScanTask(new File(projectDir, "sonar-project.properties")) + .start(); + } + + private File copyProject(String path) throws Exception { + File projectDir = temp.newFolder(); + File originalProjectDir = new File(IssueModeAndReportsMediumTest.class.getResource(path).toURI()); + FileUtils.copyDirectory(originalProjectDir, projectDir, FileFilterUtils.notFileFilter(FileFilterUtils.nameFileFilter(".sonar"))); + return projectDir; + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/fs/ProjectBuilderMediumTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/fs/ProjectBuilderMediumTest.java new file mode 100644 index 00000000000..21526ddcfd5 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/fs/ProjectBuilderMediumTest.java @@ -0,0 +1,168 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.mediumtest.fs; + +import com.google.common.collect.ImmutableMap; +import java.io.File; +import java.io.IOException; +import java.util.Date; +import java.util.List; +import org.apache.commons.io.FileUtils; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.rules.TemporaryFolder; +import org.sonar.api.utils.MessageException; +import org.sonar.batch.mediumtest.BatchMediumTester; +import org.sonar.batch.mediumtest.TaskResult; +import org.sonar.scanner.protocol.output.ScannerReport.Issue; +import org.sonar.xoo.XooPlugin; +import org.sonar.xoo.rule.XooRulesDefinition; + +import static org.assertj.core.api.Assertions.assertThat; + +public class ProjectBuilderMediumTest { + + @Rule + public TemporaryFolder temp = new TemporaryFolder(); + + @Rule + public ExpectedException exception = ExpectedException.none(); + + public BatchMediumTester tester = BatchMediumTester.builder() + .registerPlugin("xoo", new XooPlugin()) + .addRules(new XooRulesDefinition()) + .addDefaultQProfile("xoo", "Sonar Way") + .setPreviousAnalysisDate(new Date()) + .addActiveRule("xoo", "OneIssuePerLine", null, "One issue per line", "MAJOR", "OneIssuePerLine.internal", "xoo") + .build(); + + @Before + public void prepare() { + tester.start(); + } + + @After + public void stop() { + tester.stop(); + } + + @Test + public void testProjectBuilder() throws IOException { + File baseDir = prepareProject(); + + TaskResult result = tester.newTask() + .properties(ImmutableMap.<String, String>builder() + .put("sonar.task", "scan") + .put("sonar.projectBaseDir", baseDir.getAbsolutePath()) + .put("sonar.projectKey", "com.foo.project") + .put("sonar.projectName", "Foo Project") + .put("sonar.projectVersion", "1.0-SNAPSHOT") + .put("sonar.projectDescription", "Description of Foo Project") + .put("sonar.sources", ".") + .put("sonar.xoo.enableProjectBuilder", "true") + .build()) + .start(); + List<Issue> issues = result.issuesFor(result.inputFile("src/sample.xoo")); + assertThat(issues).hasSize(10); + + boolean foundIssueAtLine1 = false; + for (Issue issue : issues) { + if (issue.getLine() == 1) { + foundIssueAtLine1 = true; + assertThat(issue.getMsg()).isEqualTo("This issue is generated on each line"); + assertThat(issue.hasGap()).isFalse(); + } + } + assertThat(foundIssueAtLine1).isTrue(); + + } + + @Test + // SONAR-6976 + public void testProjectBuilderWithNewLine() throws IOException { + File baseDir = prepareProject(); + + exception.expect(MessageException.class); + exception.expectMessage("is not a valid branch name"); + tester.newTask() + .properties(ImmutableMap.<String, String>builder() + .put("sonar.task", "scan") + .put("sonar.projectBaseDir", baseDir.getAbsolutePath()) + .put("sonar.projectKey", "com.foo.project") + .put("sonar.projectName", "Foo Project") + .put("sonar.branch", "branch\n") + .put("sonar.projectVersion", "1.0-SNAPSHOT") + .put("sonar.projectDescription", "Description of Foo Project") + .put("sonar.sources", ".") + .put("sonar.xoo.enableProjectBuilder", "true") + .build()) + .start(); + } + + @Test + public void testProjectBuilderWithBranch() throws IOException { + File baseDir = prepareProject(); + + TaskResult result = tester.newTask() + .properties(ImmutableMap.<String, String>builder() + .put("sonar.task", "scan") + .put("sonar.projectBaseDir", baseDir.getAbsolutePath()) + .put("sonar.projectKey", "com.foo.project") + .put("sonar.projectName", "Foo Project") + .put("sonar.projectVersion", "1.0-SNAPSHOT") + .put("sonar.projectDescription", "Description of Foo Project") + .put("sonar.branch", "my-branch") + .put("sonar.sources", ".") + .put("sonar.xoo.enableProjectBuilder", "true") + .build()) + .start(); + + List<Issue> issues = result.issuesFor(result.inputFile("src/sample.xoo")); + assertThat(issues).hasSize(10); + + boolean foundIssueAtLine1 = false; + for (Issue issue : issues) { + if (issue.getLine() == 1) { + foundIssueAtLine1 = true; + assertThat(issue.getMsg()).isEqualTo("This issue is generated on each line"); + assertThat(issue.hasGap()).isFalse(); + } + } + assertThat(foundIssueAtLine1).isTrue(); + } + + private File prepareProject() throws IOException { + File baseDir = temp.getRoot(); + File module1Dir = new File(baseDir, "module1"); + module1Dir.mkdir(); + + File srcDir = new File(module1Dir, "src"); + srcDir.mkdir(); + + File xooFile = new File(srcDir, "sample.xoo"); + FileUtils.write(xooFile, "1\n2\n3\n4\n5\n6\n7\n8\n9\n10"); + + return baseDir; + } + +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/highlighting/HighlightingMediumTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/highlighting/HighlightingMediumTest.java new file mode 100644 index 00000000000..3c83ed61fcf --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/highlighting/HighlightingMediumTest.java @@ -0,0 +1,133 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.mediumtest.highlighting; + +import com.google.common.collect.ImmutableMap; +import java.io.File; +import java.io.IOException; +import org.apache.commons.io.FileUtils; +import org.hamcrest.Description; +import org.hamcrest.TypeSafeMatcher; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.rules.TemporaryFolder; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.sensor.highlighting.TypeOfText; +import org.sonar.batch.mediumtest.BatchMediumTester; +import org.sonar.batch.mediumtest.TaskResult; +import org.sonar.xoo.XooPlugin; + +import static org.assertj.core.api.Assertions.assertThat; + +public class HighlightingMediumTest { + + @Rule + public TemporaryFolder temp = new TemporaryFolder(); + + @Rule + public ExpectedException exception = ExpectedException.none(); + + public BatchMediumTester tester = BatchMediumTester.builder() + .registerPlugin("xoo", new XooPlugin()) + .addDefaultQProfile("xoo", "Sonar Way") + .build(); + + @Before + public void prepare() { + tester.start(); + } + + @After + public void stop() { + tester.stop(); + } + + @Test + public void computeSyntaxHighlightingOnTempProject() throws IOException { + + File baseDir = temp.newFolder(); + File srcDir = new File(baseDir, "src"); + srcDir.mkdir(); + + File xooFile = new File(srcDir, "sample.xoo"); + File xoohighlightingFile = new File(srcDir, "sample.xoo.highlighting"); + FileUtils.write(xooFile, "Sample xoo\ncontent plop"); + FileUtils.write(xoohighlightingFile, "0:10:s\n11:18:k"); + + TaskResult result = tester.newTask() + .properties(ImmutableMap.<String, String>builder() + .put("sonar.projectBaseDir", baseDir.getAbsolutePath()) + .put("sonar.projectKey", "com.foo.project") + .put("sonar.projectName", "Foo Project") + .put("sonar.projectVersion", "1.0-SNAPSHOT") + .put("sonar.projectDescription", "Description of Foo Project") + .put("sonar.sources", "src") + .build()) + .start(); + + InputFile file = result.inputFile("src/sample.xoo"); + assertThat(result.highlightingTypeFor(file, 1, 0)).containsExactly(TypeOfText.STRING); + assertThat(result.highlightingTypeFor(file, 1, 9)).containsExactly(TypeOfText.STRING); + assertThat(result.highlightingTypeFor(file, 2, 0)).containsExactly(TypeOfText.KEYWORD); + assertThat(result.highlightingTypeFor(file, 2, 8)).isEmpty(); + } + + @Test + public void computeInvalidOffsets() throws IOException { + + File baseDir = temp.newFolder(); + File srcDir = new File(baseDir, "src"); + srcDir.mkdir(); + + File xooFile = new File(srcDir, "sample.xoo"); + File xoohighlightingFile = new File(srcDir, "sample.xoo.highlighting"); + FileUtils.write(xooFile, "Sample xoo\ncontent plop"); + FileUtils.write(xoohighlightingFile, "0:10:s\n18:18:k"); + + exception.expect(IllegalStateException.class); + exception.expectMessage("Error processing line 2"); + exception.expectCause(new TypeSafeMatcher<IllegalArgumentException>() { + @Override + public void describeTo(Description description) { + description.appendText("Invalid cause"); + } + + @Override + protected boolean matchesSafely(IllegalArgumentException e) { + return e.getMessage().contains("Unable to highlight file"); + } + }); + + TaskResult result = tester.newTask() + .properties(ImmutableMap.<String, String>builder() + .put("sonar.projectBaseDir", baseDir.getAbsolutePath()) + .put("sonar.projectKey", "com.foo.project") + .put("sonar.projectName", "Foo Project") + .put("sonar.projectVersion", "1.0-SNAPSHOT") + .put("sonar.projectDescription", "Description of Foo Project") + .put("sonar.sources", "src") + .build()) + .start(); + } + +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/issues/ChecksMediumTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/issues/ChecksMediumTest.java new file mode 100644 index 00000000000..804a66a7d4d --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/issues/ChecksMediumTest.java @@ -0,0 +1,127 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.mediumtest.issues; + +import com.google.common.collect.ImmutableMap; +import java.io.File; +import java.io.IOException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import javax.annotation.Nullable; +import org.apache.commons.io.FileUtils; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.sonar.api.rule.RuleKey; +import org.sonar.batch.mediumtest.BatchMediumTester; +import org.sonar.batch.mediumtest.TaskResult; +import org.sonar.batch.rule.LoadedActiveRule; +import org.sonar.scanner.protocol.output.ScannerReport.Issue; +import org.sonar.xoo.XooPlugin; +import org.sonar.xoo.rule.XooRulesDefinition; + +import static org.assertj.core.api.Assertions.assertThat; + +public class ChecksMediumTest { + + @org.junit.Rule + public TemporaryFolder temp = new TemporaryFolder(); + + public BatchMediumTester tester = BatchMediumTester.builder() + .registerPlugin("xoo", new XooPlugin()) + .addRules(new XooRulesDefinition()) + .addDefaultQProfile("xoo", "Sonar Way") + .addRule("TemplateRule_1234", "xoo", "TemplateRule_1234", "A template rule") + .addRule("TemplateRule_1235", "xoo", "TemplateRule_1235", "Another template rule") + .activateRule(createActiveRuleWithParam("xoo", "TemplateRule_1234", "TemplateRule", "A template rule", "MAJOR", null, "xoo", "line", "1")) + .activateRule(createActiveRuleWithParam("xoo", "TemplateRule_1235", "TemplateRule", "Another template rule", "MAJOR", null, "xoo", "line", "2")) + .build(); + + @Before + public void prepare() { + tester.start(); + } + + @After + public void stop() { + tester.stop(); + } + + @Test + public void testCheckWithTemplate() throws IOException { + + File baseDir = temp.getRoot(); + File srcDir = new File(baseDir, "src"); + srcDir.mkdir(); + + File xooFile = new File(srcDir, "sample.xoo"); + FileUtils.write(xooFile, "foo\nbar"); + + TaskResult result = tester.newTask() + .properties(ImmutableMap.<String, String>builder() + .put("sonar.task", "scan") + .put("sonar.projectBaseDir", baseDir.getAbsolutePath()) + .put("sonar.projectKey", "com.foo.project") + .put("sonar.projectName", "Foo Project") + .put("sonar.projectVersion", "1.0-SNAPSHOT") + .put("sonar.projectDescription", "Description of Foo Project") + .put("sonar.sources", "src") + .build()) + .start(); + + List<Issue> issues = result.issuesFor(result.inputFile("src/sample.xoo")); + assertThat(issues).hasSize(2); + + boolean foundIssueAtLine1 = false; + boolean foundIssueAtLine2 = false; + for (Issue issue : issues) { + if (issue.getLine() == 1) { + foundIssueAtLine1 = true; + assertThat(issue.getMsg()).isEqualTo("A template rule"); + } + if (issue.getLine() == 2) { + foundIssueAtLine2 = true; + assertThat(issue.getMsg()).isEqualTo("Another template rule"); + } + } + assertThat(foundIssueAtLine1).isTrue(); + assertThat(foundIssueAtLine2).isTrue(); + } + + private LoadedActiveRule createActiveRuleWithParam(String repositoryKey, String ruleKey, @Nullable String templateRuleKey, String name, @Nullable String severity, + @Nullable String internalKey, @Nullable String languag, String paramKey, String paramValue) { + LoadedActiveRule r = new LoadedActiveRule(); + + r.setInternalKey(internalKey); + r.setRuleKey(RuleKey.of(repositoryKey, ruleKey)); + r.setName(name); + r.setTemplateRuleKey(templateRuleKey); + r.setLanguage(languag); + r.setSeverity(severity); + + Map<String, String> params = new HashMap<>(); + params.put(paramKey, paramValue); + r.setParams(params); + return r; + } + +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/issues/IssuesIssuesModeMediumTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/issues/IssuesIssuesModeMediumTest.java new file mode 100644 index 00000000000..cae361869cb --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/issues/IssuesIssuesModeMediumTest.java @@ -0,0 +1,100 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.mediumtest.issues; + +import org.junit.rules.TemporaryFolder; +import org.sonar.batch.bootstrapper.IssueListener; +import org.junit.After; +import org.junit.Before; +import com.google.common.collect.ImmutableMap; +import org.sonar.api.CoreProperties; +import org.sonar.batch.mediumtest.BatchMediumTester; +import org.sonar.xoo.XooPlugin; +import org.sonar.xoo.rule.XooRulesDefinition; +import org.apache.commons.io.FileUtils; +import org.junit.Test; +import org.sonar.batch.mediumtest.TaskResult; + +import java.io.File; +import java.util.Date; +import java.util.LinkedList; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +public class IssuesIssuesModeMediumTest { + @org.junit.Rule + public TemporaryFolder temp = new TemporaryFolder(); + + public BatchMediumTester testerPreview = BatchMediumTester.builder() + .registerPlugin("xoo", new XooPlugin()) + .addDefaultQProfile("xoo", "Sonar Way") + .addRules(new XooRulesDefinition()) + .bootstrapProperties(ImmutableMap.of(CoreProperties.ANALYSIS_MODE, CoreProperties.ANALYSIS_MODE_ISSUES)) + .addActiveRule("xoo", "OneIssuePerLine", null, "One issue per line", "MAJOR", "OneIssuePerLine.internal", "xoo") + .setLastBuildDate(new Date()) + .build(); + + @Before + public void prepare() { + testerPreview.start(); + } + + @After + public void stop() { + testerPreview.stop(); + } + + @Test + public void testIssueCallback() throws Exception { + File projectDir = new File(IssuesMediumTest.class.getResource("/mediumtest/xoo/sample").toURI()); + File tmpDir = temp.getRoot(); + FileUtils.copyDirectory(projectDir, tmpDir); + IssueRecorder issueListener = new IssueRecorder(); + + TaskResult result1 = testerPreview + .newScanTask(new File(tmpDir, "sonar-project.properties")) + .setIssueListener(issueListener) + .property(CoreProperties.ANALYSIS_MODE, CoreProperties.ANALYSIS_MODE_ISSUES) + .start(); + + assertThat(result1.trackedIssues()).hasSize(14); + assertThat(issueListener.issueList).hasSize(14); + issueListener = new IssueRecorder(); + + TaskResult result2 = testerPreview + .newScanTask(new File(tmpDir, "sonar-project.properties")) + .setIssueListener(issueListener) + .property(CoreProperties.ANALYSIS_MODE, CoreProperties.ANALYSIS_MODE_ISSUES) + .start(); + + assertThat(result2.trackedIssues()).hasSize(14); + assertThat(issueListener.issueList).hasSize(14); + } + + private class IssueRecorder implements IssueListener { + List<Issue> issueList = new LinkedList<>(); + + @Override + public void handle(Issue issue) { + issueList.add(issue); + } + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/issues/IssuesMediumTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/issues/IssuesMediumTest.java new file mode 100644 index 00000000000..50823ff4f48 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/issues/IssuesMediumTest.java @@ -0,0 +1,187 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.mediumtest.issues; + +import com.google.common.collect.ImmutableMap; +import java.io.File; +import java.io.IOException; +import java.util.LinkedList; +import java.util.List; +import org.apache.commons.io.FileUtils; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.sonar.batch.bootstrapper.IssueListener; +import org.sonar.batch.mediumtest.BatchMediumTester; +import org.sonar.batch.mediumtest.TaskResult; +import org.sonar.scanner.protocol.output.ScannerReport.Issue; +import org.sonar.xoo.XooPlugin; +import org.sonar.xoo.rule.XooRulesDefinition; + +import static org.assertj.core.api.Assertions.assertThat; + +public class IssuesMediumTest { + + @org.junit.Rule + public TemporaryFolder temp = new TemporaryFolder(); + + public BatchMediumTester tester = BatchMediumTester.builder() + .registerPlugin("xoo", new XooPlugin()) + .addDefaultQProfile("xoo", "Sonar Way") + .addRules(new XooRulesDefinition()) + .addActiveRule("xoo", "OneIssuePerLine", null, "One issue per line", "MAJOR", "OneIssuePerLine.internal", "xoo") + .build(); + + @Before + public void prepare() { + tester.start(); + } + + @After + public void stop() { + tester.stop(); + } + + @Test + public void testNoIssueCallbackInNonPreview() throws Exception { + File projectDir = new File(IssuesMediumTest.class.getResource("/mediumtest/xoo/sample").toURI()); + File tmpDir = temp.getRoot(); + FileUtils.copyDirectory(projectDir, tmpDir); + IssueRecorder issueListener = new IssueRecorder(); + + TaskResult result = tester + .newScanTask(new File(tmpDir, "sonar-project.properties")) + .setIssueListener(issueListener) + .start(); + + assertThat(result.issuesFor(result.inputFile("xources/hello/HelloJava.xoo"))).hasSize(8); + assertThat(issueListener.issueList).hasSize(0); + } + + @Test + public void testOneIssuePerLine() throws Exception { + File projectDir = new File(IssuesMediumTest.class.getResource("/mediumtest/xoo/sample").toURI()); + File tmpDir = temp.newFolder(); + FileUtils.copyDirectory(projectDir, tmpDir); + + TaskResult result = tester + .newScanTask(new File(tmpDir, "sonar-project.properties")) + .start(); + + List<Issue> issues = result.issuesFor(result.inputFile("xources/hello/HelloJava.xoo")); + assertThat(issues).hasSize(8 /* lines */); + + Issue issue = issues.get(0); + assertThat(issue.getTextRange().getStartLine()).isEqualTo(issue.getLine()); + assertThat(issue.getTextRange().getEndLine()).isEqualTo(issue.getLine()); + } + + @Test + public void findActiveRuleByInternalKey() throws Exception { + File projectDir = new File(IssuesMediumTest.class.getResource("/mediumtest/xoo/sample").toURI()); + File tmpDir = temp.newFolder(); + FileUtils.copyDirectory(projectDir, tmpDir); + + TaskResult result = tester + .newScanTask(new File(tmpDir, "sonar-project.properties")) + .property("sonar.xoo.internalKey", "OneIssuePerLine.internal") + .start(); + + List<Issue> issues = result.issuesFor(result.inputFile("xources/hello/HelloJava.xoo")); + assertThat(issues).hasSize(8 /* lines */ + 1 /* file */); + } + + @Test + public void testOverrideQProfileSeverity() throws Exception { + File projectDir = new File(IssuesMediumTest.class.getResource("/mediumtest/xoo/sample").toURI()); + File tmpDir = temp.newFolder(); + FileUtils.copyDirectory(projectDir, tmpDir); + + TaskResult result = tester + .newScanTask(new File(tmpDir, "sonar-project.properties")) + .property("sonar.oneIssuePerLine.forceSeverity", "CRITICAL") + .start(); + + List<Issue> issues = result.issuesFor(result.inputFile("xources/hello/HelloJava.xoo")); + assertThat(issues.get(0).getSeverity()).isEqualTo(org.sonar.scanner.protocol.Constants.Severity.CRITICAL); + } + + @Test + public void testIssueExclusion() throws Exception { + File projectDir = new File(IssuesMediumTest.class.getResource("/mediumtest/xoo/sample").toURI()); + File tmpDir = temp.newFolder(); + FileUtils.copyDirectory(projectDir, tmpDir); + + TaskResult result = tester + .newScanTask(new File(tmpDir, "sonar-project.properties")) + .property("sonar.issue.ignore.allfile", "1") + .property("sonar.issue.ignore.allfile.1.fileRegexp", "object") + .start(); + + assertThat(result.issuesFor(result.inputFile("xources/hello/HelloJava.xoo"))).hasSize(8 /* lines */); + assertThat(result.issuesFor(result.inputFile("xources/hello/helloscala.xoo"))).isEmpty(); + } + + @Test + public void testIssueDetails() throws IOException { + + File baseDir = temp.newFolder(); + File srcDir = new File(baseDir, "src"); + srcDir.mkdir(); + + File xooFile = new File(srcDir, "sample.xoo"); + FileUtils.write(xooFile, "1\n2\n3\n4\n5\n6\n7\n8\n9\n10"); + + TaskResult result = tester.newTask() + .properties(ImmutableMap.<String, String>builder() + .put("sonar.task", "scan") + .put("sonar.projectBaseDir", baseDir.getAbsolutePath()) + .put("sonar.projectKey", "com.foo.project") + .put("sonar.projectName", "Foo Project") + .put("sonar.projectVersion", "1.0-SNAPSHOT") + .put("sonar.projectDescription", "Description of Foo Project") + .put("sonar.sources", "src") + .build()) + .start(); + + List<Issue> issues = result.issuesFor(result.inputFile("src/sample.xoo")); + assertThat(issues).hasSize(10); + + boolean foundIssueAtLine1 = false; + for (Issue issue : issues) { + if (issue.getLine() == 1) { + foundIssueAtLine1 = true; + assertThat(issue.getMsg()).isEqualTo("This issue is generated on each line"); + assertThat(issue.hasGap()).isFalse(); + } + } + assertThat(foundIssueAtLine1).isTrue(); + } + + private class IssueRecorder implements IssueListener { + List<Issue> issueList = new LinkedList<>(); + + @Override + public void handle(Issue issue) { + issueList.add(issue); + } + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/issues/IssuesOnDirMediumTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/issues/IssuesOnDirMediumTest.java new file mode 100644 index 00000000000..aae11ca0e39 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/issues/IssuesOnDirMediumTest.java @@ -0,0 +1,113 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.mediumtest.issues; + +import com.google.common.collect.ImmutableMap; +import java.io.File; +import java.io.IOException; +import org.apache.commons.io.FileUtils; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.sonar.batch.mediumtest.BatchMediumTester; +import org.sonar.batch.mediumtest.TaskResult; +import org.sonar.xoo.XooPlugin; +import org.sonar.xoo.rule.XooRulesDefinition; + +import static org.assertj.core.api.Assertions.assertThat; + +public class IssuesOnDirMediumTest { + + @org.junit.Rule + public TemporaryFolder temp = new TemporaryFolder(); + + public BatchMediumTester tester = BatchMediumTester.builder() + .registerPlugin("xoo", new XooPlugin()) + .addDefaultQProfile("xoo", "Sonar Way") + .addRules(new XooRulesDefinition()) + .addActiveRule("xoo", "OneIssueOnDirPerFile", null, "One issue per line", "MINOR", "xoo", "xoo") + .build(); + + @Before + public void prepare() { + tester.start(); + } + + @After + public void stop() { + tester.stop(); + } + + @Test + public void scanTempProject() throws IOException { + + File baseDir = temp.getRoot(); + File srcDir = new File(baseDir, "src"); + srcDir.mkdir(); + + File xooFile1 = new File(srcDir, "sample1.xoo"); + FileUtils.write(xooFile1, "Sample1 xoo\ncontent"); + + File xooFile2 = new File(srcDir, "sample2.xoo"); + FileUtils.write(xooFile2, "Sample2 xoo\ncontent"); + + TaskResult result = tester.newTask() + .properties(ImmutableMap.<String, String>builder() + .put("sonar.task", "scan") + .put("sonar.projectBaseDir", baseDir.getAbsolutePath()) + .put("sonar.projectKey", "com.foo.project") + .put("sonar.projectName", "Foo Project") + .put("sonar.projectVersion", "1.0-SNAPSHOT") + .put("sonar.projectDescription", "Description of Foo Project") + .put("sonar.sources", "src") + .build()) + .start(); + + assertThat(result.issuesFor(result.inputDir("src"))).hasSize(2); + } + + @Test + public void issueOnRootFolder() throws IOException { + + File baseDir = temp.getRoot(); + + File xooFile1 = new File(baseDir, "sample1.xoo"); + FileUtils.write(xooFile1, "Sample1 xoo\ncontent"); + + File xooFile2 = new File(baseDir, "sample2.xoo"); + FileUtils.write(xooFile2, "Sample2 xoo\ncontent"); + + TaskResult result = tester.newTask() + .properties(ImmutableMap.<String, String>builder() + .put("sonar.task", "scan") + .put("sonar.projectBaseDir", baseDir.getAbsolutePath()) + .put("sonar.projectKey", "com.foo.project") + .put("sonar.projectName", "Foo Project") + .put("sonar.projectVersion", "1.0-SNAPSHOT") + .put("sonar.projectDescription", "Description of Foo Project") + .put("sonar.sources", ".") + .build()) + .start(); + + assertThat(result.issuesFor(result.inputDir(""))).hasSize(2); + } + +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/issues/IssuesOnModuleMediumTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/issues/IssuesOnModuleMediumTest.java new file mode 100644 index 00000000000..73434ed872d --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/issues/IssuesOnModuleMediumTest.java @@ -0,0 +1,84 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.mediumtest.issues; + +import com.google.common.collect.ImmutableMap; +import java.io.File; +import java.io.IOException; +import org.apache.commons.io.FileUtils; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.sonar.batch.mediumtest.BatchMediumTester; +import org.sonar.batch.mediumtest.TaskResult; +import org.sonar.xoo.XooPlugin; +import org.sonar.xoo.rule.XooRulesDefinition; + +import static org.assertj.core.api.Assertions.assertThat; + +public class IssuesOnModuleMediumTest { + + @org.junit.Rule + public TemporaryFolder temp = new TemporaryFolder(); + + public BatchMediumTester tester = BatchMediumTester.builder() + .registerPlugin("xoo", new XooPlugin()) + .addDefaultQProfile("xoo", "Sonar Way") + .addRules(new XooRulesDefinition()) + .addActiveRule("xoo", "OneIssuePerModule", null, "One issue per module", "MINOR", "xoo", "xoo") + .build(); + + @Before + public void prepare() { + tester.start(); + } + + @After + public void stop() { + tester.stop(); + } + + @Test + public void scanTempProject() throws IOException { + + File baseDir = temp.getRoot(); + File srcDir = new File(baseDir, "src"); + srcDir.mkdir(); + + File xooFile1 = new File(srcDir, "sample1.xoo"); + FileUtils.write(xooFile1, "Sample1 xoo\ncontent"); + + TaskResult result = tester.newTask() + .properties(ImmutableMap.<String, String>builder() + .put("sonar.task", "scan") + .put("sonar.projectBaseDir", baseDir.getAbsolutePath()) + .put("sonar.projectKey", "com.foo.project") + .put("sonar.projectName", "Foo Project") + .put("sonar.projectVersion", "1.0-SNAPSHOT") + .put("sonar.projectDescription", "Description of Foo Project") + .put("sonar.sources", "src") + .build()) + .start(); + + assertThat(result.issuesFor(result.getReportComponent("com.foo.project"))).hasSize(1); + } + +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/issues/MultilineIssuesMediumTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/issues/MultilineIssuesMediumTest.java new file mode 100644 index 00000000000..7ec0fe90535 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/issues/MultilineIssuesMediumTest.java @@ -0,0 +1,131 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.mediumtest.issues; + +import java.io.File; +import java.util.List; +import org.apache.commons.io.FileUtils; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.sonar.batch.mediumtest.BatchMediumTester; +import org.sonar.batch.mediumtest.TaskResult; +import org.sonar.scanner.protocol.output.ScannerReport.Flow; +import org.sonar.scanner.protocol.output.ScannerReport.Issue; +import org.sonar.scanner.protocol.output.ScannerReport.IssueLocation; +import org.sonar.xoo.XooPlugin; +import org.sonar.xoo.rule.XooRulesDefinition; + +import static org.assertj.core.api.Assertions.assertThat; + +public class MultilineIssuesMediumTest { + + @org.junit.Rule + public TemporaryFolder temp = new TemporaryFolder(); + + public BatchMediumTester tester = BatchMediumTester.builder() + .registerPlugin("xoo", new XooPlugin()) + .addRules(new XooRulesDefinition()) + .addDefaultQProfile("xoo", "Sonar Way") + .addActiveRule("xoo", "MultilineIssue", null, "Multinile Issue", "MAJOR", null, "xoo") + .build(); + + private TaskResult result; + + @Before + public void prepare() throws Exception { + tester.start(); + + File projectDir = new File(MultilineIssuesMediumTest.class.getResource("/mediumtest/xoo/sample-multiline").toURI()); + File tmpDir = temp.getRoot(); + FileUtils.copyDirectory(projectDir, tmpDir); + + result = tester + .newScanTask(new File(tmpDir, "sonar-project.properties")) + .start(); + } + + @After + public void stop() { + tester.stop(); + } + + @Test + public void testIssueRange() throws Exception { + List<Issue> issues = result.issuesFor(result.inputFile("xources/hello/Single.xoo")); + assertThat(issues).hasSize(1); + Issue issue = issues.get(0); + assertThat(issue.getLine()).isEqualTo(6); + assertThat(issue.getMsg()).isEqualTo("Primary location"); + assertThat(issue.getTextRange().getStartLine()).isEqualTo(6); + assertThat(issue.getTextRange().getStartOffset()).isEqualTo(23); + assertThat(issue.getTextRange().getEndLine()).isEqualTo(6); + assertThat(issue.getTextRange().getEndOffset()).isEqualTo(50); + } + + @Test + public void testMultilineIssueRange() throws Exception { + List<Issue> issues = result.issuesFor(result.inputFile("xources/hello/Multiline.xoo")); + assertThat(issues).hasSize(1); + Issue issue = issues.get(0); + assertThat(issue.getLine()).isEqualTo(6); + assertThat(issue.getMsg()).isEqualTo("Primary location"); + assertThat(issue.getTextRange().getStartLine()).isEqualTo(6); + assertThat(issue.getTextRange().getStartOffset()).isEqualTo(23); + assertThat(issue.getTextRange().getEndLine()).isEqualTo(7); + assertThat(issue.getTextRange().getEndOffset()).isEqualTo(23); + } + + @Test + public void testFlowWithSingleLocation() throws Exception { + List<Issue> issues = result.issuesFor(result.inputFile("xources/hello/Multiple.xoo")); + assertThat(issues).hasSize(1); + Issue issue = issues.get(0); + assertThat(issue.getLine()).isEqualTo(6); + assertThat(issue.getMsg()).isEqualTo("Primary location"); + assertThat(issue.getTextRange().getStartLine()).isEqualTo(6); + assertThat(issue.getTextRange().getStartOffset()).isEqualTo(23); + assertThat(issue.getTextRange().getEndLine()).isEqualTo(6); + assertThat(issue.getTextRange().getEndOffset()).isEqualTo(50); + + assertThat(issue.getFlowList()).hasSize(1); + Flow flow = issue.getFlow(0); + assertThat(flow.getLocationList()).hasSize(1); + IssueLocation additionalLocation = flow.getLocation(0); + assertThat(additionalLocation.getMsg()).isEqualTo("Flow step #1"); + assertThat(additionalLocation.getTextRange().getStartLine()).isEqualTo(7); + assertThat(additionalLocation.getTextRange().getStartOffset()).isEqualTo(26); + assertThat(additionalLocation.getTextRange().getEndLine()).isEqualTo(7); + assertThat(additionalLocation.getTextRange().getEndOffset()).isEqualTo(53); + } + + @Test + public void testFlowsWithMultipleElements() throws Exception { + List<Issue> issues = result.issuesFor(result.inputFile("xources/hello/WithFlow.xoo")); + assertThat(issues).hasSize(1); + Issue issue = issues.get(0); + assertThat(issue.getFlowList()).hasSize(1); + + Flow flow = issue.getFlow(0); + assertThat(flow.getLocationList()).hasSize(2); + // TODO more assertions + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/issuesmode/EmptyFileTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/issuesmode/EmptyFileTest.java new file mode 100644 index 00000000000..22eb34b4c22 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/issuesmode/EmptyFileTest.java @@ -0,0 +1,94 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.mediumtest.issuesmode; + +import org.sonar.batch.issue.tracking.TrackedIssue; + +import org.apache.commons.io.filefilter.FileFilterUtils; +import org.apache.commons.io.FileUtils; +import org.sonar.xoo.rule.XooRulesDefinition; +import com.google.common.collect.ImmutableMap; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.sonar.api.CoreProperties; +import org.sonar.api.utils.log.LogTester; +import org.sonar.batch.mediumtest.BatchMediumTester; +import org.sonar.batch.mediumtest.TaskResult; +import org.sonar.xoo.XooPlugin; + +import java.io.File; +import java.util.Date; + +import static org.assertj.core.api.Assertions.assertThat; + +public class EmptyFileTest { + + @Rule + public TemporaryFolder temp = new TemporaryFolder(); + + @Rule + public LogTester logTester = new LogTester(); + + public BatchMediumTester tester = BatchMediumTester.builder() + .bootstrapProperties(ImmutableMap.of(CoreProperties.ANALYSIS_MODE, CoreProperties.ANALYSIS_MODE_ISSUES)) + .registerPlugin("xoo", new XooPlugin()) + .addRules(new XooRulesDefinition()) + .addDefaultQProfile("xoo", "Sonar Way") + .addActiveRule("xoo", "OneIssuePerLine", null, "One issue per line", "MAJOR", "my/internal/key", "xoo") + .setPreviousAnalysisDate(new Date()) + .build(); + + @Before + public void prepare() { + tester.start(); + } + + @After + public void stop() { + tester.stop(); + } + + @Test + public void testIssueTrackingWithIssueOnEmptyFile() throws Exception { + File projectDir = copyProject("/mediumtest/xoo/sample-with-empty-file"); + + TaskResult result = tester + .newScanTask(new File(projectDir, "sonar-project.properties")) + .property("sonar.xoo.internalKey", "my/internal/key") + .start(); + + for(TrackedIssue i : result.trackedIssues()) { + System.out.println(i.startLine() + " " + i.getMessage()); + } + + assertThat(result.trackedIssues()).hasSize(11); + } + + private File copyProject(String path) throws Exception { + File projectDir = temp.newFolder(); + File originalProjectDir = new File(EmptyFileTest.class.getResource(path).toURI()); + FileUtils.copyDirectory(originalProjectDir, projectDir, FileFilterUtils.notFileFilter(FileFilterUtils.nameFileFilter(".sonar"))); + return projectDir; + } + +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/issuesmode/IssueModeAndReportsMediumTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/issuesmode/IssueModeAndReportsMediumTest.java new file mode 100644 index 00000000000..e02c84ec71a --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/issuesmode/IssueModeAndReportsMediumTest.java @@ -0,0 +1,316 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.mediumtest.issuesmode; + +import org.apache.commons.lang.StringUtils; + +import org.sonar.api.utils.log.LoggerLevel; +import org.assertj.core.api.Condition; +import org.sonar.batch.issue.tracking.TrackedIssue; +import com.google.common.collect.ImmutableMap; + +import java.io.File; +import java.io.IOException; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.LinkedList; +import java.util.List; + +import org.apache.commons.codec.digest.DigestUtils; +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.filefilter.FileFilterUtils; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.sonar.api.CoreProperties; +import org.sonar.api.utils.log.LogTester; +import org.sonar.batch.bootstrapper.IssueListener; +import org.sonar.batch.mediumtest.BatchMediumTester; +import org.sonar.batch.mediumtest.TaskResult; +import org.sonar.batch.scan.report.ConsoleReport; +import org.sonar.scanner.protocol.Constants.Severity; +import org.sonar.scanner.protocol.input.ScannerInput.ServerIssue; +import org.sonar.xoo.XooPlugin; +import org.sonar.xoo.rule.XooRulesDefinition; +import static org.assertj.core.api.Assertions.assertThat; + +public class IssueModeAndReportsMediumTest { + + @org.junit.Rule + public TemporaryFolder temp = new TemporaryFolder(); + + @org.junit.Rule + public LogTester logTester = new LogTester(); + + private static SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy"); + + private static Long date(String date) { + try { + return sdf.parse(date).getTime(); + } catch (ParseException e) { + throw new IllegalStateException(e); + } + } + + public BatchMediumTester tester = BatchMediumTester.builder() + .bootstrapProperties(ImmutableMap.of(CoreProperties.ANALYSIS_MODE, CoreProperties.ANALYSIS_MODE_ISSUES)) + .registerPlugin("xoo", new XooPlugin()) + .addDefaultQProfile("xoo", "Sonar Way") + .addRules(new XooRulesDefinition()) + .addRule("manual:MyManualIssue", "manual", "MyManualIssue", "My manual issue") + .addRule("manual:MyManualIssueDup", "manual", "MyManualIssue", "My manual issue") + .addActiveRule("xoo", "OneIssuePerLine", null, "One issue per line", "MAJOR", null, "xoo") + .addActiveRule("xoo", "OneIssueOnDirPerFile", null, "OneIssueOnDirPerFile", "MAJOR", null, "xoo") + .addActiveRule("xoo", "OneIssuePerModule", null, "OneIssuePerModule", "MAJOR", null, "xoo") + .addActiveRule("manual", "MyManualIssue", null, "My manual issue", "MAJOR", null, null) + .setPreviousAnalysisDate(new Date()) + // Existing issue that is still detected + .mockServerIssue(ServerIssue.newBuilder().setKey("xyz") + .setModuleKey("sample") + .setPath("xources/hello/HelloJava.xoo") + .setRuleRepository("xoo") + .setRuleKey("OneIssuePerLine") + .setLine(1) + .setSeverity(Severity.MAJOR) + .setCreationDate(date("14/03/2004")) + .setChecksum(DigestUtils.md5Hex("packagehello;")) + .setStatus("OPEN") + .build()) + // Existing issue that is no more detected (will be closed) + .mockServerIssue(ServerIssue.newBuilder().setKey("resolved") + .setModuleKey("sample") + .setPath("xources/hello/HelloJava.xoo") + .setRuleRepository("xoo") + .setRuleKey("OneIssuePerLine") + .setLine(1) + .setSeverity(Severity.MAJOR) + .setCreationDate(date("14/03/2004")) + .setChecksum(DigestUtils.md5Hex("dontexist")) + .setStatus("OPEN") + .build()) + // Existing issue on project that is still detected + .mockServerIssue(ServerIssue.newBuilder().setKey("resolved-on-project") + .setModuleKey("sample") + .setRuleRepository("xoo") + .setRuleKey("OneIssuePerModule") + .setSeverity(Severity.CRITICAL) + .setCreationDate(date("14/03/2004")) + .setStatus("OPEN") + .build()) + // Manual issue + .mockServerIssue(ServerIssue.newBuilder().setKey("manual") + .setModuleKey("sample") + .setPath("xources/hello/HelloJava.xoo") + .setRuleRepository("manual") + .setRuleKey("MyManualIssue") + .setLine(1) + .setSeverity(Severity.MAJOR) + .setCreationDate(date("14/03/2004")) + .setChecksum(DigestUtils.md5Hex("packagehello;")) + .setStatus("OPEN") + .build()) + .build(); + + @Before + public void prepare() { + tester.start(); + } + + @After + public void stop() { + tester.stop(); + } + + private File copyProject(String path) throws Exception { + File projectDir = temp.newFolder(); + File originalProjectDir = new File(IssueModeAndReportsMediumTest.class.getResource(path).toURI()); + FileUtils.copyDirectory(originalProjectDir, projectDir, FileFilterUtils.notFileFilter(FileFilterUtils.nameFileFilter(".sonar"))); + return projectDir; + } + + @Test + public void testIssueTracking() throws Exception { + File projectDir = copyProject("/mediumtest/xoo/sample"); + + TaskResult result = tester + .newScanTask(new File(projectDir, "sonar-project.properties")) + .start(); + + int newIssues = 0; + int openIssues = 0; + int resolvedIssue = 0; + for (TrackedIssue issue : result.trackedIssues()) { + System.out + .println(issue.getMessage() + " " + issue.key() + " " + issue.getRuleKey() + " " + issue.isNew() + " " + issue.resolution() + " " + issue.componentKey() + " " + + issue.startLine()); + if (issue.isNew()) { + newIssues++; + } else if (issue.resolution() != null) { + resolvedIssue++; + } else { + openIssues++; + } + } + System.out.println("new: " + newIssues + " open: " + openIssues + " resolved " + resolvedIssue); + assertThat(newIssues).isEqualTo(16); + assertThat(openIssues).isEqualTo(3); + assertThat(resolvedIssue).isEqualTo(1); + + // progress report + String logs = StringUtils.join(logTester.logs(LoggerLevel.INFO), "\n"); + + assertThat(logs).contains("Performing issue tracking"); + assertThat(logs).contains("6/6 components tracked"); + + // assert that original fields of a matched issue are kept + assertThat(result.trackedIssues()).haveExactly(1, new Condition<TrackedIssue>() { + @Override + public boolean matches(TrackedIssue value) { + return value.isNew() == false + && "resolved-on-project".equals(value.key()) + && "OPEN".equals(value.status()) + && new Date(date("14/03/2004")).equals(value.creationDate()); + } + }); + } + + @Test + public void testConsoleReport() throws Exception { + File projectDir = copyProject("/mediumtest/xoo/sample"); + + tester + .newScanTask(new File(projectDir, "sonar-project.properties")) + .property("sonar.issuesReport.console.enable", "true") + .start(); + + assertThat(getReportLog()).contains("+16 issues", "+16 major"); + } + + @Test + public void testPostJob() throws Exception { + File projectDir = copyProject("/mediumtest/xoo/sample"); + + tester + .newScanTask(new File(projectDir, "sonar-project.properties")) + .property("sonar.xoo.enablePostJob", "true") + .start(); + + assertThat(logTester.logs()).contains("Resolved issues: 1", "Open issues: 19"); + } + + private String getReportLog() { + for (String log : logTester.logs()) { + if (log.contains(ConsoleReport.HEADER)) { + return log; + } + } + throw new IllegalStateException("No console report"); + } + + @Test + public void testHtmlReport() throws Exception { + File projectDir = copyProject("/mediumtest/xoo/sample"); + + tester + .newScanTask(new File(projectDir, "sonar-project.properties")) + .property("sonar.issuesReport.html.enable", "true") + .start(); + + assertThat(new File(projectDir, ".sonar/issues-report/issues-report.html")).exists(); + assertThat(new File(projectDir, ".sonar/issues-report/issues-report-light.html")).exists(); + } + + @Test + public void testHtmlReportNoFile() throws Exception { + File baseDir = temp.newFolder(); + File srcDir = new File(baseDir, "src"); + srcDir.mkdir(); + + tester.newTask() + .properties(ImmutableMap.<String, String>builder() + .put("sonar.task", "scan") + .put("sonar.projectBaseDir", baseDir.getAbsolutePath()) + .put("sonar.projectKey", "sample") + .put("sonar.projectName", "Foo Project") + .put("sonar.projectVersion", "1.0-SNAPSHOT") + .put("sonar.projectDescription", "Description of Foo Project") + .put("sonar.sources", "src") + .put("sonar.issuesReport.html.enable", "true") + .build()) + .start(); + + assertThat(FileUtils.readFileToString(new File(baseDir, ".sonar/issues-report/issues-report.html"))).contains("No file analyzed"); + assertThat(FileUtils.readFileToString(new File(baseDir, ".sonar/issues-report/issues-report-light.html"))).contains("No file analyzed"); + } + + @Test + public void testIssueCallback() throws Exception { + File projectDir = copyProject("/mediumtest/xoo/sample"); + IssueRecorder issueListener = new IssueRecorder(); + + TaskResult result = tester + .newScanTask(new File(projectDir, "sonar-project.properties")) + .setIssueListener(issueListener) + .start(); + + assertThat(result.trackedIssues()).hasSize(20); + assertThat(issueListener.issueList).hasSize(20); + } + + private class IssueRecorder implements IssueListener { + List<Issue> issueList = new LinkedList<>(); + + @Override + public void handle(Issue issue) { + issueList.add(issue); + } + } + + @Test + public void noSyntaxHighlightingInIssuesMode() throws IOException { + + File baseDir = temp.newFolder(); + File srcDir = new File(baseDir, "src"); + srcDir.mkdir(); + + File xooFile = new File(srcDir, "sample.xoo"); + File xoohighlightingFile = new File(srcDir, "sample.xoo.highlighting"); + FileUtils.write(xooFile, "Sample xoo\ncontent plop"); + FileUtils.write(xoohighlightingFile, "0:10:s\n11:18:k"); + + TaskResult result = tester.newTask() + .properties(ImmutableMap.<String, String>builder() + .put("sonar.projectBaseDir", baseDir.getAbsolutePath()) + .put("sonar.projectKey", "com.foo.project") + .put("sonar.projectName", "Foo Project") + .put("sonar.projectVersion", "1.0-SNAPSHOT") + .put("sonar.projectDescription", "Description of Foo Project") + .put("sonar.sources", "src") + .build()) + .start(); + + assertThat(result.getReportReader().hasSyntaxHighlighting(1)).isFalse(); + assertThat(result.getReportReader().hasSyntaxHighlighting(2)).isFalse(); + assertThat(result.getReportReader().hasSyntaxHighlighting(3)).isFalse(); + } + +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/issuesmode/NoPreviousAnalysisTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/issuesmode/NoPreviousAnalysisTest.java new file mode 100644 index 00000000000..fa133979562 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/issuesmode/NoPreviousAnalysisTest.java @@ -0,0 +1,86 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.mediumtest.issuesmode; + +import org.sonar.batch.mediumtest.TaskResult; +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.filefilter.FileFilterUtils; + +import java.io.File; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.google.common.collect.ImmutableMap; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.sonar.api.CoreProperties; +import org.sonar.api.utils.log.LogTester; +import org.sonar.batch.mediumtest.BatchMediumTester; +import org.sonar.xoo.XooPlugin; +import org.sonar.xoo.rule.XooRulesDefinition; + +public class NoPreviousAnalysisTest { + @Rule + public TemporaryFolder temp = new TemporaryFolder(); + + @Rule + public LogTester logTester = new LogTester(); + + public BatchMediumTester tester = BatchMediumTester.builder() + .bootstrapProperties(ImmutableMap.of(CoreProperties.ANALYSIS_MODE, CoreProperties.ANALYSIS_MODE_ISSUES)) + .registerPlugin("xoo", new XooPlugin()) + .addRules(new XooRulesDefinition()) + .addDefaultQProfile("xoo", "Sonar Way") + .addActiveRule("xoo", "OneIssuePerLine", null, "One issue per line", "MAJOR", "my/internal/key", "xoo") + .setPreviousAnalysisDate(null) + .build(); + + @Before + public void prepare() { + tester.start(); + } + + @After + public void stop() { + tester.stop(); + } + + @Test + public void testIssueTrackingWithIssueOnEmptyFile() throws Exception { + File projectDir = copyProject("/mediumtest/xoo/sample"); + + TaskResult result = tester + .newScanTask(new File(projectDir, "sonar-project.properties")) + .start(); + + assertThat(result.trackedIssues()).hasSize(14); + + } + + private File copyProject(String path) throws Exception { + File projectDir = temp.newFolder(); + File originalProjectDir = new File(IssueModeAndReportsMediumTest.class.getResource(path).toURI()); + FileUtils.copyDirectory(originalProjectDir, projectDir, FileFilterUtils.notFileFilter(FileFilterUtils.nameFileFilter(".sonar"))); + return projectDir; + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/issuesmode/NonAssociatedProject.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/issuesmode/NonAssociatedProject.java new file mode 100644 index 00000000000..32c66f2689f --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/issuesmode/NonAssociatedProject.java @@ -0,0 +1,89 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.mediumtest.issuesmode; + +import com.google.common.collect.ImmutableMap; +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.filefilter.FileFilterUtils; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.sonar.api.CoreProperties; +import org.sonar.api.utils.log.LogTester; +import org.sonar.batch.mediumtest.BatchMediumTester; +import org.sonar.batch.mediumtest.TaskResult; +import org.sonar.xoo.XooPlugin; +import org.sonar.xoo.rule.XooRulesDefinition; + +import java.io.File; +import java.io.IOException; + +public class NonAssociatedProject { + @org.junit.Rule + public TemporaryFolder temp = new TemporaryFolder(); + + @org.junit.Rule + public LogTester logTester = new LogTester(); + + public BatchMediumTester tester; + + @Before + public void prepare() throws IOException { + tester = BatchMediumTester.builder() + .bootstrapProperties(ImmutableMap.of( + CoreProperties.ANALYSIS_MODE, CoreProperties.ANALYSIS_MODE_ISSUES, + CoreProperties.GLOBAL_WORKING_DIRECTORY, temp.newFolder().getAbsolutePath())) + .registerPlugin("xoo", new XooPlugin()) + .addQProfile("xoo", "Sonar Way") + .addRules(new XooRulesDefinition()) + .addRule("manual:MyManualIssue", "manual", "MyManualIssue", "My manual issue") + .addRule("manual:MyManualIssueDup", "manual", "MyManualIssue", "My manual issue") + .addActiveRule("xoo", "OneIssuePerLine", null, "One issue per line", "MAJOR", null, "xoo") + .addActiveRule("xoo", "OneIssueOnDirPerFile", null, "OneIssueOnDirPerFile", "MAJOR", null, "xoo") + .addActiveRule("xoo", "OneIssuePerModule", null, "OneIssuePerModule", "MAJOR", null, "xoo") + .addActiveRule("manual", "MyManualIssue", null, "My manual issue", "MAJOR", null, null) + .setAssociated(false) + .build(); + tester.start(); + } + + @After + public void stop() { + tester.stop(); + } + + private File copyProject(String path) throws Exception { + File projectDir = temp.newFolder(); + File originalProjectDir = new File(IssueModeAndReportsMediumTest.class.getResource(path).toURI()); + FileUtils.copyDirectory(originalProjectDir, projectDir, FileFilterUtils.notFileFilter(FileFilterUtils.nameFileFilter(".sonar"))); + return projectDir; + } + + @Test + public void testNonAssociated() throws Exception { + File projectDir = copyProject("/mediumtest/xoo/multi-modules-sample-not-associated"); + + TaskResult result = tester + .newScanTask(new File(projectDir, "sonar-project.properties")) + .start(); + + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/issuesmode/ScanOnlyChangedTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/issuesmode/ScanOnlyChangedTest.java new file mode 100644 index 00000000000..eb5850b0809 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/issuesmode/ScanOnlyChangedTest.java @@ -0,0 +1,212 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.mediumtest.issuesmode; + +import org.sonar.batch.issue.tracking.TrackedIssue; + +import org.assertj.core.api.Condition; +import com.google.common.io.Resources; +import org.sonar.batch.repository.FileData; +import org.sonar.scanner.protocol.Constants.Severity; +import org.sonar.scanner.protocol.input.ScannerInput.ServerIssue; +import com.google.common.collect.ImmutableMap; +import org.apache.commons.codec.digest.DigestUtils; +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.filefilter.FileFilterUtils; +import org.junit.After; +import org.junit.Before; +import org.sonar.api.CoreProperties; +import org.sonar.batch.mediumtest.BatchMediumTester; +import org.sonar.xoo.XooPlugin; +import org.sonar.xoo.rule.XooRulesDefinition; +import org.junit.rules.TemporaryFolder; +import org.sonar.api.utils.log.LogTester; +import org.junit.Test; +import org.sonar.batch.mediumtest.TaskResult; + +import java.io.File; +import java.io.IOException; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; + +import static org.assertj.core.api.Assertions.assertThat; + +public class ScanOnlyChangedTest { + @org.junit.Rule + public TemporaryFolder temp = new TemporaryFolder(); + + @org.junit.Rule + public LogTester logTester = new LogTester(); + + private static SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy"); + + private BatchMediumTester tester; + + private static Long date(String date) { + try { + return sdf.parse(date).getTime(); + } catch (ParseException e) { + throw new IllegalStateException(e); + } + } + + @Before + public void prepare() throws IOException { + String filePath = "xources/hello/HelloJava.xoo"; + String md5sum = DigestUtils.md5Hex(FileUtils.readFileToString(new File( + Resources.getResource("mediumtest/xoo/sample/" + filePath).getPath()))); + + tester = BatchMediumTester.builder() + .bootstrapProperties(ImmutableMap.of(CoreProperties.ANALYSIS_MODE, CoreProperties.ANALYSIS_MODE_ISSUES)) + .registerPlugin("xoo", new XooPlugin()) + .addDefaultQProfile("xoo", "Sonar Way") + .addRules(new XooRulesDefinition()) + .addRule("manual:MyManualIssue", "manual", "MyManualIssue", "My manual issue") + .addRule("manual:MyManualIssueDup", "manual", "MyManualIssue", "My manual issue") + .addActiveRule("xoo", "OneIssuePerLine", null, "One issue per line", "MAJOR", null, "xoo") + .addActiveRule("xoo", "OneIssueOnDirPerFile", null, "OneIssueOnDirPerFile", "MAJOR", null, "xoo") + .addActiveRule("xoo", "OneIssuePerModule", null, "OneIssuePerModule", "MAJOR", null, "xoo") + .addActiveRule("manual", "MyManualIssue", null, "My manual issue", "MAJOR", null, null) + // this will cause the file to have status==SAME + .addFileData("sample", filePath, new FileData(md5sum, null)) + .setPreviousAnalysisDate(new Date()) + // Existing issue that is copied + .mockServerIssue(ServerIssue.newBuilder().setKey("xyz") + .setModuleKey("sample") + .setMsg("One issue per Line copied") + .setPath("xources/hello/HelloJava.xoo") + .setRuleRepository("xoo") + .setRuleKey("OneIssuePerLine") + .setLine(1) + .setSeverity(Severity.MAJOR) + .setCreationDate(date("14/03/2004")) + .setChecksum(DigestUtils.md5Hex("packagehello;")) + .setStatus("OPEN") + .build()) + // Existing issue on project that is still detected + .mockServerIssue(ServerIssue.newBuilder().setKey("resolved-on-project") + .setModuleKey("sample") + .setRuleRepository("xoo") + .setRuleKey("OneIssuePerModule") + .setSeverity(Severity.CRITICAL) + .setCreationDate(date("14/03/2004")) + .setStatus("OPEN") + .build()) + // Manual issue + .mockServerIssue(ServerIssue.newBuilder().setKey("manual") + .setModuleKey("sample") + .setPath("xources/hello/HelloJava.xoo") + .setRuleRepository("manual") + .setRuleKey("MyManualIssue") + .setLine(1) + .setSeverity(Severity.MAJOR) + .setCreationDate(date("14/03/2004")) + .setChecksum(DigestUtils.md5Hex("packagehello;")) + .setStatus("OPEN") + .build()) + .build(); + tester.start(); + } + + @After + public void stop() { + tester.stop(); + } + + private File copyProject(String path) throws Exception { + File projectDir = temp.newFolder(); + File originalProjectDir = new File(IssueModeAndReportsMediumTest.class.getResource(path).toURI()); + FileUtils.copyDirectory(originalProjectDir, projectDir, FileFilterUtils.notFileFilter(FileFilterUtils.nameFileFilter(".sonar"))); + return projectDir; + } + + @Test + public void testScanAll() throws Exception { + File projectDir = copyProject("/mediumtest/xoo/sample"); + + TaskResult result = tester + .newScanTask(new File(projectDir, "sonar-project.properties")) + .property("sonar.scanAllFiles", "true") + .start(); + + assertNumberIssues(result, 16, 3, 0); + + /* + * 8 new per line + * 1 manual + */ + assertNumberIssuesOnFile(result, "HelloJava.xoo", 9); + } + + @Test + public void testScanOnlyChangedFiles() throws Exception { + File projectDir = copyProject("/mediumtest/xoo/sample"); + + TaskResult result = tester + .newScanTask(new File(projectDir, "sonar-project.properties")) + .start(); + + /* + * We have: + * 6 new issues per line (open) in helloscala.xoo + * 2 new issues per file in helloscala.xoo / ClassOneTest.xoo + * 1 server issue (open, not new) copied from server in HelloJava.xoo (this file is unchanged) + * 1 manual issue (open, not new) in HelloJava.xoo + * 1 existing issue on the project (open, not new) + */ + assertNumberIssues(result, 8, 3, 0); + + // should only have server issues (HelloJava.xoo should not have been analyzed) + assertNumberIssuesOnFile(result, "HelloJava.xoo", 2); + } + + private static void assertNumberIssuesOnFile(TaskResult result, final String fileNameEndsWith, int issues) { + assertThat(result.trackedIssues()).haveExactly(issues, new Condition<TrackedIssue>() { + @Override + public boolean matches(TrackedIssue value) { + return value.componentKey().endsWith(fileNameEndsWith); + } + }); + } + + private static void assertNumberIssues(TaskResult result, int expectedNew, int expectedOpen, int expectedResolved) { + int newIssues = 0; + int openIssues = 0; + int resolvedIssue = 0; + for (TrackedIssue issue : result.trackedIssues()) { + System.out + .println(issue.getMessage() + " " + issue.key() + " " + issue.getRuleKey() + " " + issue.isNew() + " " + issue.resolution() + " " + issue.componentKey() + " " + + issue.startLine()); + if (issue.isNew()) { + newIssues++; + } else if (issue.resolution() != null) { + resolvedIssue++; + } else { + openIssues++; + } + } + + System.out.println("new: " + newIssues + " open: " + openIssues + " resolved " + resolvedIssue); + assertThat(newIssues).isEqualTo(expectedNew); + assertThat(openIssues).isEqualTo(expectedOpen); + assertThat(resolvedIssue).isEqualTo(expectedResolved); + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/log/ExceptionHandlingMediumTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/log/ExceptionHandlingMediumTest.java new file mode 100644 index 00000000000..db1a2dcd30e --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/log/ExceptionHandlingMediumTest.java @@ -0,0 +1,116 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.mediumtest.log; + +import java.util.Collections; + +import org.hamcrest.Matchers; +import org.hamcrest.Description; +import org.hamcrest.TypeSafeMatcher; +import org.junit.BeforeClass; +import org.sonar.batch.bootstrapper.EnvironmentInformation; +import org.sonar.api.utils.MessageException; +import org.apache.commons.lang.mutable.MutableBoolean; +import org.sonar.batch.repository.GlobalRepositoriesLoader; +import org.sonar.scanner.protocol.input.GlobalRepositories; +import org.sonar.batch.bootstrapper.Batch; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +public class ExceptionHandlingMediumTest { + @Rule + public ExpectedException thrown = ExpectedException.none(); + + private Batch batch; + private static ErrorGlobalRepositoriesLoader loader; + + @BeforeClass + public static void beforeClass() { + loader = new ErrorGlobalRepositoriesLoader(); + } + + public void setUp(boolean verbose) { + Batch.Builder builder = Batch.builder() + .setEnableLoggingConfiguration(true) + .addComponents( + loader, + new EnvironmentInformation("mediumTest", "1.0")); + + if (verbose) { + builder.setBootstrapProperties(Collections.singletonMap("sonar.verbose", "true")); + } + batch = builder.build(); + } + + @Test + public void test() throws Exception { + setUp(false); + loader.withCause = false; + thrown.expect(MessageException.class); + thrown.expectMessage("Error loading repository"); + thrown.expectCause(Matchers.nullValue(Throwable.class)); + + batch.start(); + } + + @Test + public void testWithCause() throws Exception { + setUp(false); + loader.withCause = true; + + thrown.expect(MessageException.class); + thrown.expectMessage("Error loading repository"); + thrown.expectCause(new TypeSafeMatcher<Throwable>() { + @Override + public void describeTo(Description description) { + } + + @Override + protected boolean matchesSafely(Throwable item) { + return item instanceof IllegalStateException && item.getMessage().equals("Code 401"); + } + }); + + batch.start(); + } + + @Test + public void testWithVerbose() throws Exception { + setUp(true); + thrown.expect(IllegalStateException.class); + thrown.expectMessage("Unable to load component class"); + batch.start(); + } + + private static class ErrorGlobalRepositoriesLoader implements GlobalRepositoriesLoader { + boolean withCause = false; + + @Override + public GlobalRepositories load(MutableBoolean fromCache) { + if (withCause) { + IllegalStateException cause = new IllegalStateException("Code 401"); + throw MessageException.of("Error loading repository", cause); + } else { + throw MessageException.of("Error loading repository"); + } + } + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/log/LogListenerTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/log/LogListenerTest.java new file mode 100644 index 00000000000..f360dca56ac --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/log/LogListenerTest.java @@ -0,0 +1,216 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.mediumtest.log; + +import com.google.common.collect.ImmutableMap; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.PrintStream; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.apache.commons.io.FileUtils; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.rules.TemporaryFolder; +import org.sonar.batch.bootstrapper.LogOutput; +import org.sonar.batch.mediumtest.BatchMediumTester; +import org.sonar.xoo.XooPlugin; +import static org.assertj.core.api.Assertions.assertThat; + +public class LogListenerTest { + @Rule + public TemporaryFolder temp = new TemporaryFolder(); + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + private Pattern simpleTimePattern = Pattern.compile("\\d{2}:\\d{2}:\\d{2}"); + private List<LogEvent> logOutput; + private StringBuilder logOutputStr; + private ByteArrayOutputStream stdOutTarget = new ByteArrayOutputStream(); + private ByteArrayOutputStream stdErrTarget = new ByteArrayOutputStream(); + private static PrintStream savedStdOut; + private static PrintStream savedStdErr; + + public BatchMediumTester tester = BatchMediumTester.builder() + .registerPlugin("xoo", new XooPlugin()) + .addDefaultQProfile("xoo", "Sonar Way") + .setLogOutput(new SimpleLogListener()) + .build(); + + private File baseDir; + + private ImmutableMap.Builder<String, String> builder; + + @BeforeClass + public static void backupStdStreams() { + savedStdOut = System.out; + savedStdErr = System.err; + } + + @AfterClass + public static void resumeStdStreams() { + if (savedStdOut != null) { + System.setOut(savedStdOut); + } + if (savedStdErr != null) { + System.setErr(savedStdErr); + } + } + + @Before + public void prepare() throws IOException { + System.setOut(new PrintStream(stdOutTarget)); + System.setErr(new PrintStream(stdErrTarget)); + // logger from the batch might write to it asynchronously + logOutput = Collections.synchronizedList(new LinkedList<LogEvent>()); + logOutputStr = new StringBuilder(); + tester.start(); + + baseDir = temp.getRoot(); + + builder = ImmutableMap.<String, String>builder() + .put("sonar.task", "scan") + .put("sonar.projectBaseDir", baseDir.getAbsolutePath()) + .put("sonar.projectKey", "com.foo.project") + .put("sonar.projectName", "Foo Project") + .put("sonar.projectVersion", "1.0-SNAPSHOT") + .put("sonar.projectDescription", "Description of Foo Project"); + } + + private void assertNoStdOutput() { + assertThat(stdOutTarget.toByteArray()).isEmpty(); + assertThat(stdErrTarget.toByteArray()).isEmpty(); + } + + /** + * Check that log message is not formatted, i.e. has no log level and timestamp. + */ + private void assertMsgClean(String msg) { + for (LogOutput.Level l : LogOutput.Level.values()) { + assertThat(msg).doesNotContain(l.toString()); + } + + Matcher matcher = simpleTimePattern.matcher(msg); + assertThat(matcher.find()).isFalse(); + } + + @Test + public void testChangeLogForAnalysis() throws IOException, InterruptedException { + File srcDir = new File(baseDir, "src"); + srcDir.mkdir(); + + File xooFile = new File(srcDir, "sample.xoo"); + FileUtils.write(xooFile, "Sample xoo\ncontent"); + + tester.newTask() + .properties(builder + .put("sonar.sources", "src") + .put("sonar.verbose", "true") + .build()) + .start(); + + tester.stop(); + for (LogEvent e : logOutput) { + savedStdOut.println("[captured]" + e.level + " " + e.msg); + } + + // only done in DEBUG during analysis + assertThat(logOutputStr.toString()).contains("Post-jobs : "); + } + + @Test + public void testNoStdLog() throws IOException { + File srcDir = new File(baseDir, "src"); + srcDir.mkdir(); + + File xooFile = new File(srcDir, "sample.xoo"); + FileUtils.write(xooFile, "Sample xoo\ncontent"); + + tester.newTask() + .properties(builder + .put("sonar.sources", "src") + .build()) + .start(); + tester.stop(); + + assertNoStdOutput(); + assertThat(logOutput).isNotEmpty(); + + synchronized (logOutput) { + for (LogEvent e : logOutput) { + savedStdOut.println("[captured]" + e.level + " " + e.msg); + } + } + } + + @Test + public void testNoFormattedMsgs() throws IOException { + File srcDir = new File(baseDir, "src"); + srcDir.mkdir(); + + File xooFile = new File(srcDir, "sample.xoo"); + FileUtils.write(xooFile, "Sample xoo\ncontent"); + + tester.newTask() + .properties(builder + .put("sonar.sources", "src") + .build()) + .start(); + tester.stop(); + + assertNoStdOutput(); + + synchronized (logOutput) { + for (LogEvent e : logOutput) { + assertMsgClean(e.msg); + savedStdOut.println("[captured]" + e.level + " " + e.msg); + } + } + } + + private class SimpleLogListener implements LogOutput { + @Override + public void log(String msg, Level level) { + logOutput.add(new LogEvent(msg, level)); + logOutputStr.append(msg).append("\n"); + } + } + + private static class LogEvent { + String msg; + LogOutput.Level level; + + LogEvent(String msg, LogOutput.Level level) { + this.msg = msg; + this.level = level; + } + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/measures/MeasuresMediumTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/measures/MeasuresMediumTest.java new file mode 100644 index 00000000000..e9db30c49f4 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/measures/MeasuresMediumTest.java @@ -0,0 +1,131 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.mediumtest.measures; + +import com.google.common.collect.ImmutableMap; +import java.io.File; +import java.io.IOException; +import java.util.List; +import java.util.Map; +import org.apache.commons.io.FileUtils; +import org.assertj.core.groups.Tuple; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.sonar.api.measures.CoreMetrics; +import org.sonar.batch.mediumtest.BatchMediumTester; +import org.sonar.batch.mediumtest.TaskResult; +import org.sonar.scanner.protocol.output.ScannerReport.Measure; +import org.sonar.xoo.XooPlugin; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.tuple; + +public class MeasuresMediumTest { + + @org.junit.Rule + public TemporaryFolder temp = new TemporaryFolder(); + + private File baseDir; + private File srcDir; + + public BatchMediumTester tester = BatchMediumTester.builder() + .registerPlugin("xoo", new XooPlugin()) + .addDefaultQProfile("xoo", "Sonar Way") + .build(); + + @Before + public void prepare() { + tester.start(); + } + + @After + public void stop() { + tester.stop(); + } + + @Before + public void setUp() { + baseDir = temp.getRoot(); + srcDir = new File(baseDir, "src"); + srcDir.mkdir(); + } + + @Test + public void computeMeasuresOnTempProject() throws IOException { + File xooFile = new File(srcDir, "sample.xoo"); + File xooMeasureFile = new File(srcDir, "sample.xoo.measures"); + FileUtils.write(xooFile, "Sample xoo\ncontent"); + FileUtils.write(xooMeasureFile, "lines:20"); + + TaskResult result = tester.newTask() + .properties(ImmutableMap.<String, String>builder() + .put("sonar.task", "scan") + .put("sonar.projectBaseDir", baseDir.getAbsolutePath()) + .put("sonar.projectKey", "com.foo.project") + .put("sonar.projectName", "Foo Project") + .put("sonar.projectVersion", "1.0-SNAPSHOT") + .put("sonar.projectDescription", "Description of Foo Project") + .put("sonar.sources", "src") + .put("sonar.cpd.xoo.skip", "true") + .build()) + .start(); + + Map<String, List<Measure>> allMeasures = result.allMeasures(); + + assertThat(allMeasures.get("com.foo.project")).extracting("metricKey", "intValue", "doubleValue", "stringValue").containsOnly( + Tuple.tuple(CoreMetrics.QUALITY_PROFILES_KEY, 0, 0.0, + "[{\"key\":\"Sonar Way\",\"language\":\"xoo\",\"name\":\"Sonar Way\",\"rulesUpdatedAt\":\"2009-02-13T23:31:31+0000\"}]")); + + assertThat(allMeasures.get("com.foo.project:src/sample.xoo")).extracting("metricKey", "intValue").containsOnly( + Tuple.tuple(CoreMetrics.LINES_KEY, 2)); + } + + @Test + public void computeLinesOnAllFiles() throws IOException { + File xooFile = new File(srcDir, "sample.xoo"); + FileUtils.write(xooFile, "Sample xoo\n\ncontent"); + + File otherFile = new File(srcDir, "sample.other"); + FileUtils.write(otherFile, "Sample other\ncontent\n"); + + TaskResult result = tester.newTask() + .properties(ImmutableMap.<String, String>builder() + .put("sonar.task", "scan") + .put("sonar.projectBaseDir", baseDir.getAbsolutePath()) + .put("sonar.projectKey", "com.foo.project") + .put("sonar.projectName", "Foo Project") + .put("sonar.projectVersion", "1.0-SNAPSHOT") + .put("sonar.projectDescription", "Description of Foo Project") + .put("sonar.sources", "src") + .put("sonar.import_unknown_files", "true") + .build()) + .start(); + + Map<String, List<Measure>> allMeasures = result.allMeasures(); + + assertThat(allMeasures.get("com.foo.project:src/sample.xoo")).extracting("metricKey", "intValue") + .contains(tuple("lines", 3)); + assertThat(allMeasures.get("com.foo.project:src/sample.other")).extracting("metricKey", "intValue") + .contains(tuple("lines", 3), tuple("ncloc", 2)); + } + +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/scm/ScmMediumTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/scm/ScmMediumTest.java new file mode 100644 index 00000000000..37c8da1ef01 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/scm/ScmMediumTest.java @@ -0,0 +1,363 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.mediumtest.scm; + +import com.google.common.collect.ImmutableMap; +import java.io.File; +import java.io.IOException; +import org.apache.commons.codec.digest.DigestUtils; +import org.apache.commons.io.FileUtils; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.rules.TemporaryFolder; +import org.sonar.api.utils.PathUtils; +import org.sonar.api.utils.log.LogTester; +import org.sonar.batch.mediumtest.BatchMediumTester; +import org.sonar.batch.mediumtest.BatchMediumTester.TaskBuilder; +import org.sonar.batch.repository.FileData; +import org.sonar.scanner.protocol.output.ScannerReport; +import org.sonar.scanner.protocol.output.ScannerReportReader; +import org.sonar.scanner.protocol.output.ScannerReport.Component; +import org.sonar.scanner.protocol.output.ScannerReport.Changesets.Changeset; +import org.sonar.xoo.XooPlugin; + +import static org.assertj.core.api.Assertions.assertThat; + +public class ScmMediumTest { + + private static final String MISSING_BLAME_INFORMATION_FOR_THE_FOLLOWING_FILES = "Missing blame information for the following files:"; + private static final String CHANGED_CONTENT_SCM_ON_SERVER_XOO = "src/changed_content_scm_on_server.xoo"; + private static final String SAME_CONTENT_SCM_ON_SERVER_XOO = "src/same_content_scm_on_server.xoo"; + private static final String SAME_CONTENT_NO_SCM_ON_SERVER_XOO = "src/same_content_no_scm_on_server.xoo"; + private static final String SAMPLE_XOO_CONTENT = "Sample xoo\ncontent"; + + @org.junit.Rule + public TemporaryFolder temp = new TemporaryFolder(); + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Rule + public LogTester logTester = new LogTester(); + + public BatchMediumTester tester = BatchMediumTester.builder() + .registerPlugin("xoo", new XooPlugin()) + .addDefaultQProfile("xoo", "Sonar Way") + .addFileData("com.foo.project", CHANGED_CONTENT_SCM_ON_SERVER_XOO, new FileData(DigestUtils.md5Hex(SAMPLE_XOO_CONTENT), null)) + .addFileData("com.foo.project", SAME_CONTENT_NO_SCM_ON_SERVER_XOO, new FileData(DigestUtils.md5Hex(SAMPLE_XOO_CONTENT), null)) + .addFileData("com.foo.project", SAME_CONTENT_SCM_ON_SERVER_XOO, new FileData(DigestUtils.md5Hex(SAMPLE_XOO_CONTENT), "1.1")) + .build(); + + @Before + public void prepare() { + tester.start(); + } + + @After + public void stop() { + tester.stop(); + } + + @Test + public void testScmMeasure() throws IOException { + + File baseDir = prepareProject(); + + tester.newTask() + .properties(ImmutableMap.<String, String>builder() + .put("sonar.task", "scan") + .put("sonar.projectBaseDir", baseDir.getAbsolutePath()) + .put("sonar.projectKey", "com.foo.project") + .put("sonar.projectName", "Foo Project") + .put("sonar.projectVersion", "1.0-SNAPSHOT") + .put("sonar.projectDescription", "Description of Foo Project") + .put("sonar.sources", "src") + .put("sonar.scm.provider", "xoo") + .build()) + .start(); + + ScannerReport.Changesets fileScm = getChangesets(baseDir, "src/sample.xoo"); + + assertThat(fileScm.getChangesetIndexByLineList()).hasSize(5); + + Changeset changesetLine1 = fileScm.getChangeset(fileScm.getChangesetIndexByLine(0)); + assertThat(changesetLine1.hasAuthor()).isFalse(); + + Changeset changesetLine2 = fileScm.getChangeset(fileScm.getChangesetIndexByLine(1)); + assertThat(changesetLine2.getAuthor()).isEqualTo("julien"); + + Changeset changesetLine3 = fileScm.getChangeset(fileScm.getChangesetIndexByLine(2)); + assertThat(changesetLine3.getAuthor()).isEqualTo("julien"); + + Changeset changesetLine4 = fileScm.getChangeset(fileScm.getChangesetIndexByLine(3)); + assertThat(changesetLine4.getAuthor()).isEqualTo("julien"); + + Changeset changesetLine5 = fileScm.getChangeset(fileScm.getChangesetIndexByLine(4)); + assertThat(changesetLine5.getAuthor()).isEqualTo("simon"); + } + + private ScannerReport.Changesets getChangesets(File baseDir, String path) { + File reportDir = new File(baseDir, ".sonar/batch-report"); + ScannerReportReader reader = new ScannerReportReader(reportDir); + + Component project = reader.readComponent(reader.readMetadata().getRootComponentRef()); + Component dir = reader.readComponent(project.getChildRef(0)); + for (Integer fileRef : dir.getChildRefList()) { + Component file = reader.readComponent(fileRef); + if (file.getPath().equals(path)) { + return reader.readChangesets(file.getRef()); + } + } + return null; + } + + @Test + public void noScmOnEmptyFile() throws IOException { + + File baseDir = prepareProject(); + + // Clear file content + FileUtils.write(new File(baseDir, "src/sample.xoo"), ""); + + tester.newTask() + .properties(ImmutableMap.<String, String>builder() + .put("sonar.task", "scan") + .put("sonar.projectBaseDir", baseDir.getAbsolutePath()) + .put("sonar.projectKey", "com.foo.project") + .put("sonar.projectName", "Foo Project") + .put("sonar.projectVersion", "1.0-SNAPSHOT") + .put("sonar.projectDescription", "Description of Foo Project") + .put("sonar.sources", "src") + .put("sonar.scm.provider", "xoo") + .build()) + .start(); + + ScannerReport.Changesets changesets = getChangesets(baseDir, "src/sample.xoo"); + + assertThat(changesets).isNull(); + } + + @Test + public void log_files_with_missing_blame() throws IOException { + + File baseDir = prepareProject(); + File xooFileWithoutBlame = new File(baseDir, "src/sample_no_blame.xoo"); + FileUtils.write(xooFileWithoutBlame, "Sample xoo\ncontent\n3\n4\n5"); + + tester.newTask() + .properties(ImmutableMap.<String, String>builder() + .put("sonar.task", "scan") + .put("sonar.projectBaseDir", baseDir.getAbsolutePath()) + .put("sonar.projectKey", "com.foo.project") + .put("sonar.projectName", "Foo Project") + .put("sonar.projectVersion", "1.0-SNAPSHOT") + .put("sonar.projectDescription", "Description of Foo Project") + .put("sonar.sources", "src") + .put("sonar.scm.provider", "xoo") + .build()) + .start(); + + ScannerReport.Changesets file1Scm = getChangesets(baseDir, "src/sample.xoo"); + assertThat(file1Scm).isNotNull(); + + ScannerReport.Changesets fileWithoutBlameScm = getChangesets(baseDir, "src/sample_no_blame.xoo"); + assertThat(fileWithoutBlameScm).isNull(); + + assertThat(logTester.logs()).containsSubsequence("2 files to be analyzed", "1/2 files analyzed", MISSING_BLAME_INFORMATION_FOR_THE_FOLLOWING_FILES, + " * " + PathUtils.sanitize(xooFileWithoutBlame.toPath().toString())); + } + + // SONAR-6397 + @Test + public void optimize_blame() throws IOException { + + File baseDir = prepareProject(); + File changedContentScmOnServer = new File(baseDir, CHANGED_CONTENT_SCM_ON_SERVER_XOO); + FileUtils.write(changedContentScmOnServer, SAMPLE_XOO_CONTENT + "\nchanged"); + File xooScmFile = new File(baseDir, CHANGED_CONTENT_SCM_ON_SERVER_XOO + ".scm"); + FileUtils.write(xooScmFile, + // revision,author,dateTime + "1,foo,2013-01-04\n" + + "1,bar,2013-01-04\n" + + "2,biz,2014-01-04\n"); + + File sameContentScmOnServer = new File(baseDir, SAME_CONTENT_SCM_ON_SERVER_XOO); + FileUtils.write(sameContentScmOnServer, SAMPLE_XOO_CONTENT); + // No need to write .scm file since this file should not be blamed + + File sameContentNoScmOnServer = new File(baseDir, SAME_CONTENT_NO_SCM_ON_SERVER_XOO); + FileUtils.write(sameContentNoScmOnServer, SAMPLE_XOO_CONTENT); + xooScmFile = new File(baseDir, SAME_CONTENT_NO_SCM_ON_SERVER_XOO + ".scm"); + FileUtils.write(xooScmFile, + // revision,author,dateTime + "1,foo,2013-01-04\n" + + "1,bar,2013-01-04\n"); + + tester.newTask() + .properties(ImmutableMap.<String, String>builder() + .put("sonar.task", "scan") + .put("sonar.projectBaseDir", baseDir.getAbsolutePath()) + .put("sonar.projectKey", "com.foo.project") + .put("sonar.projectName", "Foo Project") + .put("sonar.projectVersion", "1.0-SNAPSHOT") + .put("sonar.projectDescription", "Description of Foo Project") + .put("sonar.sources", "src") + .put("sonar.scm.provider", "xoo") + .build()) + .start(); + + assertThat(getChangesets(baseDir, "src/sample.xoo")).isNotNull(); + + assertThat(getChangesets(baseDir, CHANGED_CONTENT_SCM_ON_SERVER_XOO)).isNotNull(); + + assertThat(getChangesets(baseDir, SAME_CONTENT_SCM_ON_SERVER_XOO)).isNull(); + + assertThat(getChangesets(baseDir, SAME_CONTENT_NO_SCM_ON_SERVER_XOO)).isNotNull(); + + assertThat(logTester.logs()).containsSubsequence("3 files to be analyzed", "3/3 files analyzed"); + assertThat(logTester.logs()).doesNotContain(MISSING_BLAME_INFORMATION_FOR_THE_FOLLOWING_FILES); + } + + @Test + public void forceReload() throws IOException { + + File baseDir = prepareProject(); + File xooFileNoScm = new File(baseDir, SAME_CONTENT_SCM_ON_SERVER_XOO); + FileUtils.write(xooFileNoScm, SAMPLE_XOO_CONTENT); + File xooScmFile = new File(baseDir, SAME_CONTENT_SCM_ON_SERVER_XOO + ".scm"); + FileUtils.write(xooScmFile, + // revision,author,dateTime + "1,foo,2013-01-04\n" + + "1,bar,2013-01-04\n"); + + TaskBuilder taskBuilder = tester.newTask() + .properties(ImmutableMap.<String, String>builder() + .put("sonar.task", "scan") + .put("sonar.projectBaseDir", baseDir.getAbsolutePath()) + .put("sonar.projectKey", "com.foo.project") + .put("sonar.projectName", "Foo Project") + .put("sonar.projectVersion", "1.0-SNAPSHOT") + .put("sonar.projectDescription", "Description of Foo Project") + .put("sonar.sources", "src") + .put("sonar.scm.provider", "xoo") + // Force reload + .put("sonar.scm.forceReloadAll", "true") + .build()); + + taskBuilder.start(); + + ScannerReport.Changesets file1Scm = getChangesets(baseDir, "src/sample.xoo"); + assertThat(file1Scm).isNotNull(); + + ScannerReport.Changesets file2Scm = getChangesets(baseDir, SAME_CONTENT_SCM_ON_SERVER_XOO); + assertThat(file2Scm).isNotNull(); + } + + @Test + public void configureUsingScmURL() throws IOException { + + File baseDir = prepareProject(); + + tester.newTask() + .properties(ImmutableMap.<String, String>builder() + .put("sonar.task", "scan") + .put("sonar.projectBaseDir", baseDir.getAbsolutePath()) + .put("sonar.projectKey", "com.foo.project") + .put("sonar.projectName", "Foo Project") + .put("sonar.projectVersion", "1.0-SNAPSHOT") + .put("sonar.projectDescription", "Description of Foo Project") + .put("sonar.sources", "src") + .put("sonar.links.scm_dev", "scm:xoo:foobar") + .build()) + .start(); + + ScannerReport.Changesets file1Scm = getChangesets(baseDir, "src/sample.xoo"); + assertThat(file1Scm).isNotNull(); + } + + @Test + public void testAutoDetection() throws IOException { + + File baseDir = prepareProject(); + new File(baseDir, ".xoo").createNewFile(); + + tester.newTask() + .properties(ImmutableMap.<String, String>builder() + .put("sonar.task", "scan") + .put("sonar.projectBaseDir", baseDir.getAbsolutePath()) + .put("sonar.projectKey", "com.foo.project") + .put("sonar.projectName", "Foo Project") + .put("sonar.projectVersion", "1.0-SNAPSHOT") + .put("sonar.projectDescription", "Description of Foo Project") + .put("sonar.sources", "src") + .build()) + .start(); + + ScannerReport.Changesets file1Scm = getChangesets(baseDir, "src/sample.xoo"); + assertThat(file1Scm).isNotNull(); + } + + private File prepareProject() throws IOException { + File baseDir = temp.getRoot(); + File srcDir = new File(baseDir, "src"); + srcDir.mkdir(); + + File xooFile1 = new File(srcDir, "sample.xoo"); + FileUtils.write(xooFile1, "Sample xoo\ncontent\n3\n4\n5"); + File xooScmFile1 = new File(srcDir, "sample.xoo.scm"); + FileUtils.write(xooScmFile1, + // revision,author,dateTime + "1,,2013-01-04\n" + + "2,julien,2013-01-04\n" + + "3,julien,2013-02-03\n" + + "3,julien,2013-02-03\n" + + "4,simon,2013-03-04\n"); + + return baseDir; + } + + @Test + public void testDisableScmSensor() throws IOException { + + File baseDir = prepareProject(); + + tester.newTask() + .properties(ImmutableMap.<String, String>builder() + .put("sonar.task", "scan") + .put("sonar.projectBaseDir", baseDir.getAbsolutePath()) + .put("sonar.projectKey", "com.foo.project") + .put("sonar.projectName", "Foo Project") + .put("sonar.projectVersion", "1.0-SNAPSHOT") + .put("sonar.projectDescription", "Description of Foo Project") + .put("sonar.sources", "src") + .put("sonar.scm.disabled", "true") + .put("sonar.scm.provider", "xoo") + .put("sonar.cpd.xoo.skip", "true") + .build()) + .start(); + + ScannerReport.Changesets changesets = getChangesets(baseDir, "src/sample.xoo"); + assertThat(changesets).isNull(); + } + +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/symbol/SymbolMediumTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/symbol/SymbolMediumTest.java new file mode 100644 index 00000000000..8d9836f994d --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/symbol/SymbolMediumTest.java @@ -0,0 +1,116 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.mediumtest.symbol; + +import com.google.common.collect.ImmutableMap; +import java.io.File; +import java.io.IOException; +import org.apache.commons.io.FileUtils; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.batch.mediumtest.BatchMediumTester; +import org.sonar.batch.mediumtest.TaskResult; +import org.sonar.scanner.protocol.output.ScannerReport; +import org.sonar.xoo.XooPlugin; + +import static org.assertj.core.api.Assertions.assertThat; + +public class SymbolMediumTest { + + @org.junit.Rule + public TemporaryFolder temp = new TemporaryFolder(); + + public BatchMediumTester tester = BatchMediumTester.builder() + .registerPlugin("xoo", new XooPlugin()) + .addDefaultQProfile("xoo", "Sonar Way") + .build(); + + @Before + public void prepare() { + tester.start(); + } + + @After + public void stop() { + tester.stop(); + } + + @Test + public void computeSymbolReferencesOnTempProject() throws IOException { + + File baseDir = temp.getRoot(); + File srcDir = new File(baseDir, "src"); + srcDir.mkdir(); + + File xooFile = new File(srcDir, "sample.xoo"); + File xooSymbolFile = new File(srcDir, "sample.xoo.symbol"); + FileUtils.write(xooFile, "Sample xoo\ncontent\nanother xoo"); + // Highlight xoo symbol + FileUtils.write(xooSymbolFile, "7:10,27"); + + TaskResult result = tester.newTask() + .properties(ImmutableMap.<String, String>builder() + .put("sonar.task", "scan") + .put("sonar.projectBaseDir", baseDir.getAbsolutePath()) + .put("sonar.projectKey", "com.foo.project") + .put("sonar.projectName", "Foo Project") + .put("sonar.projectVersion", "1.0-SNAPSHOT") + .put("sonar.projectDescription", "Description of Foo Project") + .put("sonar.sources", "src") + .build()) + .start(); + + InputFile file = result.inputFile("src/sample.xoo"); + assertThat(result.symbolReferencesFor(file, 1, 7)).containsOnly(ScannerReport.TextRange.newBuilder().setStartLine(3).setStartOffset(8).setEndLine(3).setEndOffset(11).build()); + } + + @Test + public void computeSymbolReferencesWithVariableLength() throws IOException { + + File baseDir = temp.getRoot(); + File srcDir = new File(baseDir, "src"); + srcDir.mkdir(); + + File xooFile = new File(srcDir, "sample.xoo"); + File xooSymbolFile = new File(srcDir, "sample.xoo.symbol"); + FileUtils.write(xooFile, "Sample xoo\ncontent\nanother xoo\nyet another"); + // Highlight xoo symbol + FileUtils.write(xooSymbolFile, "7:10,27:32"); + + TaskResult result = tester.newTask() + .properties(ImmutableMap.<String, String>builder() + .put("sonar.task", "scan") + .put("sonar.projectBaseDir", baseDir.getAbsolutePath()) + .put("sonar.projectKey", "com.foo.project") + .put("sonar.projectName", "Foo Project") + .put("sonar.projectVersion", "1.0-SNAPSHOT") + .put("sonar.projectDescription", "Description of Foo Project") + .put("sonar.sources", "src") + .build()) + .start(); + + InputFile file = result.inputFile("src/sample.xoo"); + assertThat(result.symbolReferencesFor(file, 1, 7)).containsOnly(ScannerReport.TextRange.newBuilder().setStartLine(3).setStartOffset(8).setEndLine(4).setEndOffset(1).build()); + } + +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/tasks/TasksMediumTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/tasks/TasksMediumTest.java new file mode 100644 index 00000000000..766e9371898 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/tasks/TasksMediumTest.java @@ -0,0 +1,160 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.mediumtest.tasks; + +import com.google.common.collect.ImmutableMap; +import java.util.Arrays; +import java.util.List; +import org.assertj.core.api.Condition; +import org.junit.After; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.sonar.api.SonarPlugin; +import org.sonar.api.issue.action.Actions; +import org.sonar.api.task.Task; +import org.sonar.api.task.TaskDefinition; +import org.sonar.api.utils.MessageException; +import org.sonar.api.utils.log.LogTester; +import org.sonar.batch.bootstrap.MockHttpServer; +import org.sonar.batch.mediumtest.BatchMediumTester; + +import static org.assertj.core.api.Assertions.assertThat; + +public class TasksMediumTest { + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Rule + public LogTester logTester = new LogTester(); + + public BatchMediumTester tester = BatchMediumTester.builder() + .registerPlugin("faketask", new FakeTaskPlugin()) + .build(); + + private MockHttpServer server = null; + + @After + public void stopServer() { + if (server != null) { + server.stop(); + } + } + + @After + public void stop() { + tester.stop(); + } + + @Test + public void listTasksIncludingBroken() throws Exception { + tester.start(); + tester.newTask() + .properties(ImmutableMap.<String, String>builder() + .put("sonar.task", "list").build()) + .start(); + + assertThat(logTester.logs()).haveExactly(1, new Condition<String>() { + + @Override + public boolean matches(String value) { + return value.contains("Available tasks:") && value.contains("fake: Fake description") && value.contains("broken: Broken description"); + } + }); + } + + @Test + public void runBroken() throws Exception { + tester.start(); + + thrown.expect(IllegalStateException.class); + thrown.expectMessage( + "Unable to load component class org.sonar.batch.mediumtest.tasks.TasksMediumTest$BrokenTask"); + + tester.newTask() + .properties(ImmutableMap.<String, String>builder() + .put("sonar.task", "broken").build()) + .start(); + } + + @Test(expected = MessageException.class) + public void unsupportedTask() throws Exception { + tester = BatchMediumTester.builder() + .build(); + tester.start(); + tester.newTask() + .properties(ImmutableMap.<String, String>builder() + .put("sonar.task", "foo").build()) + .start(); + } + + private void startServer(Integer responseStatus, String responseData) throws Exception { + server = new MockHttpServer(); + server.start(); + + if (responseStatus != null) { + server.setMockResponseStatus(responseStatus); + } + if (responseData != null) { + server.setMockResponseData(responseData); + } + } + + private static class FakeTaskPlugin extends SonarPlugin { + + @Override + public List getExtensions() { + return Arrays.asList(FakeTask.DEF, FakeTask.class, BrokenTask.DEF, BrokenTask.class); + } + + } + + private static class FakeTask implements Task { + + public static final TaskDefinition DEF = TaskDefinition.builder().key("fake").description("Fake description").taskClass(FakeTask.class).build(); + + @Override + public void execute() { + // TODO Auto-generated method stub + + } + + } + + private static class BrokenTask implements Task { + + public static final TaskDefinition DEF = TaskDefinition.builder().key("broken").description("Broken description").taskClass(BrokenTask.class).build(); + private final Actions serverSideComponent; + + public BrokenTask(Actions serverSideComponent) { + this.serverSideComponent = serverSideComponent; + } + + @Override + public void execute() { + System.out.println(serverSideComponent.list()); + ; + + } + + } + +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/tests/CoveragePerTestMediumTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/tests/CoveragePerTestMediumTest.java new file mode 100644 index 00000000000..d62d978ecaf --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/tests/CoveragePerTestMediumTest.java @@ -0,0 +1,160 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.mediumtest.tests; + +import org.hamcrest.Description; + +import org.hamcrest.TypeSafeMatcher; +import org.junit.Rule; +import org.junit.rules.ExpectedException; +import com.google.common.collect.ImmutableMap; +import org.apache.commons.io.FileUtils; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.batch.mediumtest.BatchMediumTester; +import org.sonar.batch.mediumtest.TaskResult; +import org.sonar.xoo.XooPlugin; + +import java.io.File; +import java.io.IOException; + +import static org.assertj.core.api.Assertions.assertThat; + +public class CoveragePerTestMediumTest { + + @Rule + public TemporaryFolder temp = new TemporaryFolder(); + + @Rule + public ExpectedException exception = ExpectedException.none(); + + public BatchMediumTester tester = BatchMediumTester.builder() + .registerPlugin("xoo", new XooPlugin()) + .addDefaultQProfile("xoo", "Sonar Way") + .build(); + + @Before + public void prepare() { + tester.start(); + } + + @After + public void stop() { + tester.stop(); + } + + @Test + // SONAR-6183 + public void invalidCoverage() throws IOException { + File baseDir = createTestFiles(); + File srcDir = new File(baseDir, "src"); + + File coverageFile = new File(srcDir, "sample.xoo.coverage"); + FileUtils.write(coverageFile, "0:2\n"); + + exception.expect(IllegalStateException.class); + exception.expectMessage("Error processing line 1 of file"); + exception.expectCause(new TypeSafeMatcher<Throwable>() { + + @Override + public void describeTo(Description description) { + // nothing to do + } + + @Override + protected boolean matchesSafely(Throwable item) { + return item.getMessage().contains("Line number must be strictly positive"); + } + }); + runTask(baseDir); + + } + + @Test + public void coveragePerTestInReport() throws IOException { + File baseDir = createTestFiles(); + File testDir = new File(baseDir, "test"); + + File xooTestExecutionFile = new File(testDir, "sampleTest.xoo.test"); + FileUtils.write(xooTestExecutionFile, "some test:4:::OK:UNIT\n" + + "another test:10:::OK:UNIT\n" + + "test without coverage:10:::OK:UNIT\n"); + + File xooCoveragePerTestFile = new File(testDir, "sampleTest.xoo.testcoverage"); + FileUtils.write(xooCoveragePerTestFile, "some test;src/sample.xoo,10,11;src/sample2.xoo,1,2\n" + + "another test;src/sample.xoo,10,20\n"); + + TaskResult result = runTask(baseDir); + + InputFile file = result.inputFile("test/sampleTest.xoo"); + org.sonar.scanner.protocol.output.ScannerReport.CoverageDetail someTest = result.coveragePerTestFor(file, "some test"); + assertThat(someTest.getCoveredFileList()).hasSize(2); + assertThat(someTest.getCoveredFile(0).getFileRef()).isGreaterThan(0); + assertThat(someTest.getCoveredFile(0).getCoveredLineList()).containsExactly(10, 11); + assertThat(someTest.getCoveredFile(1).getFileRef()).isGreaterThan(0); + assertThat(someTest.getCoveredFile(1).getCoveredLineList()).containsExactly(1, 2); + + org.sonar.scanner.protocol.output.ScannerReport.CoverageDetail anotherTest = result.coveragePerTestFor(file, "another test"); + assertThat(anotherTest.getCoveredFileList()).hasSize(1); + assertThat(anotherTest.getCoveredFile(0).getFileRef()).isGreaterThan(0); + assertThat(anotherTest.getCoveredFile(0).getCoveredLineList()).containsExactly(10, 20); + } + + private TaskResult runTask(File baseDir) { + return tester.newTask() + .properties(ImmutableMap.<String, String>builder() + .put("sonar.task", "scan") + .put("sonar.projectBaseDir", baseDir.getAbsolutePath()) + .put("sonar.projectKey", "com.foo.project") + .put("sonar.projectName", "Foo Project") + .put("sonar.projectVersion", "1.0-SNAPSHOT") + .put("sonar.projectDescription", "Description of Foo Project") + .put("sonar.sources", "src") + .put("sonar.tests", "test") + .build()) + .start(); + } + + private File createTestFiles() throws IOException { + File baseDir = temp.getRoot(); + File srcDir = new File(baseDir, "src"); + srcDir.mkdir(); + File testDir = new File(baseDir, "test"); + testDir.mkdir(); + + File xooFile = new File(srcDir, "sample.xoo"); + FileUtils.write(xooFile, "foo"); + + File xooFile2 = new File(srcDir, "sample2.xoo"); + FileUtils.write(xooFile2, "foo"); + + File xooTestFile = new File(testDir, "sampleTest.xoo"); + FileUtils.write(xooTestFile, "failure\nerror\nok\nskipped"); + + File xooTestFile2 = new File(testDir, "sample2Test.xoo"); + FileUtils.write(xooTestFile2, "test file tests"); + + return baseDir; + } + +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/tests/TestExecutionMediumTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/tests/TestExecutionMediumTest.java new file mode 100644 index 00000000000..cae559f16c5 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/tests/TestExecutionMediumTest.java @@ -0,0 +1,105 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.mediumtest.tests; + +import com.google.common.collect.ImmutableMap; +import org.apache.commons.io.FileUtils; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.batch.mediumtest.BatchMediumTester; +import org.sonar.batch.mediumtest.TaskResult; +import org.sonar.scanner.protocol.Constants.TestStatus; +import org.sonar.xoo.XooPlugin; + +import java.io.File; +import java.io.IOException; + +import static org.assertj.core.api.Assertions.assertThat; + +public class TestExecutionMediumTest { + + @org.junit.Rule + public TemporaryFolder temp = new TemporaryFolder(); + + public BatchMediumTester tester = BatchMediumTester.builder() + .registerPlugin("xoo", new XooPlugin()) + .addDefaultQProfile("xoo", "Sonar Way") + .build(); + + @Before + public void prepare() { + tester.start(); + } + + @After + public void stop() { + tester.stop(); + } + + @Test + public void unitTests() throws IOException { + + File baseDir = temp.getRoot(); + File srcDir = new File(baseDir, "src"); + srcDir.mkdir(); + File testDir = new File(baseDir, "test"); + testDir.mkdir(); + + File xooFile = new File(srcDir, "sample.xoo"); + FileUtils.write(xooFile, "foo"); + + File xooTestFile = new File(testDir, "sampleTest.xoo"); + FileUtils.write(xooTestFile, "failure\nerror\nok\nskipped"); + + File xooTestExecutionFile = new File(testDir, "sampleTest.xoo.test"); + FileUtils.write(xooTestExecutionFile, "skipped::::SKIPPED:UNIT\n" + + "failure:2:Failure::FAILURE:UNIT\n" + + "error:2:Error:The stack:ERROR:UNIT\n" + + "success:4:::OK:INTEGRATION"); + + TaskResult result = tester.newTask() + .properties(ImmutableMap.<String, String>builder() + .put("sonar.task", "scan") + .put("sonar.projectBaseDir", baseDir.getAbsolutePath()) + .put("sonar.projectKey", "com.foo.project") + .put("sonar.projectName", "Foo Project") + .put("sonar.projectVersion", "1.0-SNAPSHOT") + .put("sonar.projectDescription", "Description of Foo Project") + .put("sonar.sources", "src") + .put("sonar.tests", "test") + .build()) + .start(); + + InputFile file = result.inputFile("test/sampleTest.xoo"); + org.sonar.scanner.protocol.output.ScannerReport.Test success = result.testExecutionFor(file, "success"); + assertThat(success.getDurationInMs()).isEqualTo(4); + assertThat(success.getStatus()).isEqualTo(TestStatus.OK); + + org.sonar.scanner.protocol.output.ScannerReport.Test error = result.testExecutionFor(file, "error"); + assertThat(error.getDurationInMs()).isEqualTo(2); + assertThat(error.getStatus()).isEqualTo(TestStatus.ERROR); + assertThat(error.getMsg()).isEqualTo("Error"); + assertThat(error.getStacktrace()).isEqualTo("The stack"); + } + +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/phases/PostJobsExecutorTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/phases/PostJobsExecutorTest.java new file mode 100644 index 00000000000..f9fda97b21c --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/phases/PostJobsExecutorTest.java @@ -0,0 +1,60 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.phases; + +import org.junit.Before; +import org.junit.Test; +import org.sonar.api.batch.PostJob; +import org.sonar.api.batch.SensorContext; +import org.sonar.api.resources.Project; +import org.sonar.batch.bootstrap.BatchExtensionDictionnary; +import org.sonar.batch.events.EventBus; + +import java.util.Arrays; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class PostJobsExecutorTest { + PostJobsExecutor executor; + + Project project = new Project("project"); + BatchExtensionDictionnary selector = mock(BatchExtensionDictionnary.class); + PostJob job1 = mock(PostJob.class); + PostJob job2 = mock(PostJob.class); + SensorContext context = mock(SensorContext.class); + + @Before + public void setUp() { + executor = new PostJobsExecutor(selector, project, mock(EventBus.class)); + } + + @Test + public void should_execute_post_jobs() { + when(selector.select(PostJob.class, project, true, null)).thenReturn(Arrays.asList(job1, job2)); + + executor.execute(context); + + verify(job1).executeOn(project, context); + verify(job2).executeOn(project, context); + + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/platform/DefaultServerTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/platform/DefaultServerTest.java new file mode 100644 index 00000000000..801848354fc --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/platform/DefaultServerTest.java @@ -0,0 +1,51 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.platform; + +import org.sonar.batch.bootstrap.GlobalProperties; + +import org.junit.Test; +import org.sonar.api.CoreProperties; +import org.sonar.api.config.Settings; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class DefaultServerTest { + + @Test + public void shouldLoadServerProperties() { + Settings settings = new Settings(); + settings.setProperty(CoreProperties.SERVER_ID, "123"); + settings.setProperty(CoreProperties.SERVER_VERSION, "2.2"); + settings.setProperty(CoreProperties.SERVER_STARTTIME, "2010-05-18T17:59:00+0000"); + settings.setProperty(CoreProperties.PERMANENT_SERVER_ID, "abcde"); + GlobalProperties props = mock(GlobalProperties.class); + when(props.property("sonar.host.url")).thenReturn("http://foo.com"); + + DefaultServer metadata = new DefaultServer(settings, props); + + assertThat(metadata.getId()).isEqualTo("123"); + assertThat(metadata.getVersion()).isEqualTo("2.2"); + assertThat(metadata.getStartedAt()).isNotNull(); + assertThat(metadata.getURL()).isEqualTo("http://foo.com"); + assertThat(metadata.getPermanentServerId()).isEqualTo("abcde"); + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/postjob/DefaultPostJobContextTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/postjob/DefaultPostJobContextTest.java new file mode 100644 index 00000000000..12f00a55d90 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/postjob/DefaultPostJobContextTest.java @@ -0,0 +1,87 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.postjob; + +import org.sonar.batch.issue.tracking.TrackedIssue; + +import java.util.Arrays; + +import org.junit.Before; +import org.junit.Test; +import org.sonar.api.batch.AnalysisMode; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.postjob.issue.Issue; +import org.sonar.api.batch.rule.Severity; +import org.sonar.api.config.Settings; +import org.sonar.api.resources.File; +import org.sonar.batch.index.BatchComponentCache; +import org.sonar.batch.issue.IssueCache; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class DefaultPostJobContextTest { + + private IssueCache issueCache; + private BatchComponentCache resourceCache; + private AnalysisMode analysisMode; + private DefaultPostJobContext context; + private Settings settings; + + @Before + public void prepare() { + issueCache = mock(IssueCache.class); + resourceCache = new BatchComponentCache(); + analysisMode = mock(AnalysisMode.class); + settings = new Settings(); + context = new DefaultPostJobContext(settings, analysisMode, issueCache, resourceCache); + } + + @Test + public void test() { + assertThat(context.settings()).isSameAs(settings); + assertThat(context.analysisMode()).isSameAs(analysisMode); + + TrackedIssue defaultIssue = new TrackedIssue(); + defaultIssue.setComponentKey("foo:src/Foo.php"); + defaultIssue.setGap(2.0); + defaultIssue.setNew(true); + defaultIssue.setKey("xyz"); + defaultIssue.setStartLine(1); + defaultIssue.setMessage("msg"); + defaultIssue.setSeverity("BLOCKER"); + when(issueCache.all()).thenReturn(Arrays.asList(defaultIssue)); + + Issue issue = context.issues().iterator().next(); + assertThat(issue.componentKey()).isEqualTo("foo:src/Foo.php"); + assertThat(issue.effortToFix()).isEqualTo(2.0); + assertThat(issue.isNew()).isTrue(); + assertThat(issue.key()).isEqualTo("xyz"); + assertThat(issue.line()).isEqualTo(1); + assertThat(issue.message()).isEqualTo("msg"); + assertThat(issue.severity()).isEqualTo(Severity.BLOCKER); + assertThat(issue.inputComponent()).isNull(); + + InputFile inputPath = mock(InputFile.class); + resourceCache.add(File.create("src/Foo.php").setEffectiveKey("foo:src/Foo.php"), null).setInputComponent(inputPath); + assertThat(issue.inputComponent()).isEqualTo(inputPath); + + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/postjob/PostJobOptimizerTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/postjob/PostJobOptimizerTest.java new file mode 100644 index 00000000000..e5d30f1dfa3 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/postjob/PostJobOptimizerTest.java @@ -0,0 +1,77 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.postjob; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.sonar.api.batch.AnalysisMode; +import org.sonar.api.batch.postjob.internal.DefaultPostJobDescriptor; +import org.sonar.api.config.Settings; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class PostJobOptimizerTest { + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + private PostJobOptimizer optimizer; + private Settings settings; + private AnalysisMode analysisMode; + + @Before + public void prepare() { + settings = new Settings(); + analysisMode = mock(AnalysisMode.class); + optimizer = new PostJobOptimizer(settings, analysisMode); + } + + @Test + public void should_run_analyzer_with_no_metadata() { + DefaultPostJobDescriptor descriptor = new DefaultPostJobDescriptor(); + + assertThat(optimizer.shouldExecute(descriptor)).isTrue(); + } + + @Test + public void should_optimize_on_settings() { + DefaultPostJobDescriptor descriptor = new DefaultPostJobDescriptor() + .requireProperty("sonar.foo.reportPath"); + assertThat(optimizer.shouldExecute(descriptor)).isFalse(); + + settings.setProperty("sonar.foo.reportPath", "foo"); + assertThat(optimizer.shouldExecute(descriptor)).isTrue(); + } + + @Test + public void should_disabled_in_issues_mode() { + DefaultPostJobDescriptor descriptor = new DefaultPostJobDescriptor() + .disabledInIssues(); + assertThat(optimizer.shouldExecute(descriptor)).isTrue(); + + when(analysisMode.isIssues()).thenReturn(true); + + assertThat(optimizer.shouldExecute(descriptor)).isFalse(); + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/profiling/PhasesSumUpTimeProfilerTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/profiling/PhasesSumUpTimeProfilerTest.java new file mode 100644 index 00000000000..9c8e4ed8d56 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/profiling/PhasesSumUpTimeProfilerTest.java @@ -0,0 +1,410 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.profiling; + +import com.google.common.collect.Maps; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.sonar.api.CoreProperties; +import org.sonar.api.batch.Decorator; +import org.sonar.api.batch.Initializer; +import org.sonar.api.batch.PostJob; +import org.sonar.api.batch.Sensor; +import org.sonar.api.batch.SensorContext; +import org.sonar.api.batch.events.DecoratorExecutionHandler; +import org.sonar.api.batch.events.DecoratorsPhaseHandler; +import org.sonar.api.batch.events.InitializerExecutionHandler; +import org.sonar.api.batch.events.InitializersPhaseHandler; +import org.sonar.api.batch.events.PostJobExecutionHandler; +import org.sonar.api.batch.events.PostJobsPhaseHandler; +import org.sonar.api.batch.events.ProjectAnalysisHandler; +import org.sonar.api.batch.events.ProjectAnalysisHandler.ProjectAnalysisEvent; +import org.sonar.api.batch.events.SensorExecutionHandler; +import org.sonar.api.batch.events.SensorExecutionHandler.SensorExecutionEvent; +import org.sonar.api.batch.events.SensorsPhaseHandler; +import org.sonar.api.batch.events.SensorsPhaseHandler.SensorsPhaseEvent; +import org.sonar.api.resources.Project; +import org.sonar.api.utils.System2; +import org.sonar.batch.bootstrap.GlobalProperties; +import org.sonar.batch.events.BatchStepEvent; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; + +public class PhasesSumUpTimeProfilerTest { + + @Rule + public TemporaryFolder temp = new TemporaryFolder(); + + private MockedSystem clock; + private PhasesSumUpTimeProfiler profiler; + + @Before + public void prepare() throws Exception { + clock = new MockedSystem(); + Map<String, String> props = Maps.newHashMap(); + props.put(CoreProperties.WORKING_DIRECTORY, temp.newFolder().getAbsolutePath()); + profiler = new PhasesSumUpTimeProfiler(clock, new GlobalProperties(props)); + } + + @Test + public void testSimpleProject() throws InterruptedException { + + final Project project = mockProject("my:project", true); + when(project.getModules()).thenReturn(Collections.<Project>emptyList()); + + fakeAnalysis(profiler, project); + + assertThat(profiler.currentModuleProfiling.getProfilingPerPhase(Phase.INIT).getProfilingPerItem(new FakeInitializer()).totalTime()).isEqualTo(7L); + assertThat(profiler.currentModuleProfiling.getProfilingPerPhase(Phase.SENSOR).getProfilingPerItem(new FakeSensor()).totalTime()).isEqualTo(10L); + assertThat(profiler.currentModuleProfiling.getProfilingPerPhase(Phase.POSTJOB).getProfilingPerItem(new FakePostJob()).totalTime()).isEqualTo(30L); + assertThat(profiler.currentModuleProfiling.getProfilingPerBatchStep("Free memory").totalTime()).isEqualTo(9L); + } + + @Test + public void testMultimoduleProject() throws InterruptedException { + final Project project = mockProject("project root", true); + final Project moduleA = mockProject("moduleA", false); + final Project moduleB = mockProject("moduleB", false); + when(project.getModules()).thenReturn(Arrays.asList(moduleA, moduleB)); + + fakeAnalysis(profiler, moduleA); + fakeAnalysis(profiler, moduleB); + fakeAnalysis(profiler, project); + + assertThat(profiler.currentModuleProfiling.getProfilingPerPhase(Phase.INIT).getProfilingPerItem(new FakeInitializer()).totalTime()).isEqualTo(7L); + assertThat(profiler.currentModuleProfiling.getProfilingPerPhase(Phase.SENSOR).getProfilingPerItem(new FakeSensor()).totalTime()).isEqualTo(10L); + assertThat(profiler.currentModuleProfiling.getProfilingPerPhase(Phase.POSTJOB).getProfilingPerItem(new FakePostJob()).totalTime()).isEqualTo(30L); + + assertThat(profiler.totalProfiling.getProfilingPerPhase(Phase.INIT).getProfilingPerItem(new FakeInitializer()).totalTime()).isEqualTo(21L); + assertThat(profiler.totalProfiling.getProfilingPerPhase(Phase.SENSOR).getProfilingPerItem(new FakeSensor()).totalTime()).isEqualTo(30L); + assertThat(profiler.totalProfiling.getProfilingPerPhase(Phase.POSTJOB).getProfilingPerItem(new FakePostJob()).totalTime()).isEqualTo(90L); + } + + @Test + public void testDisplayTimings() { + AbstractTimeProfiling profiling = new AbstractTimeProfiling(System2.INSTANCE) { + }; + + profiling.setTotalTime(5); + assertThat(profiling.totalTimeAsString()).isEqualTo("5ms"); + + profiling.setTotalTime(5 * 1000 + 12); + assertThat(profiling.totalTimeAsString()).isEqualTo("5s"); + + profiling.setTotalTime(5 * 60 * 1000 + 12 * 1000); + assertThat(profiling.totalTimeAsString()).isEqualTo("5min 12s"); + + profiling.setTotalTime(5 * 60 * 1000); + assertThat(profiling.totalTimeAsString()).isEqualTo("5min"); + } + + private class MockedSystem extends System2 { + private long now = 0; + + @Override + public long now() { + return now; + } + + public void sleep(long duration) { + now += duration; + } + } + + private Project mockProject(String name, boolean isRoot) { + final Project project = spy(new Project("myProject")); + when(project.isRoot()).thenReturn(isRoot); + when(project.getName()).thenReturn(name); + return project; + } + + private void fakeAnalysis(PhasesSumUpTimeProfiler profiler, final Project module) { + // Start of moduleA + profiler.onProjectAnalysis(projectEvent(module, true)); + initializerPhase(profiler); + sensorPhase(profiler); + postJobPhase(profiler); + batchStep(profiler); + // End of moduleA + profiler.onProjectAnalysis(projectEvent(module, false)); + } + + private void batchStep(PhasesSumUpTimeProfiler profiler) { + // Start of batch step + profiler.onBatchStep(new BatchStepEvent("Free memory", true)); + clock.sleep(9); + // End of batch step + profiler.onBatchStep(new BatchStepEvent("Free memory", false)); + } + + private void initializerPhase(PhasesSumUpTimeProfiler profiler) { + Initializer initializer = new FakeInitializer(); + // Start of initializer phase + profiler.onInitializersPhase(initializersEvent(true)); + // Start of an initializer + profiler.onInitializerExecution(initializerEvent(initializer, true)); + clock.sleep(7); + // End of an initializer + profiler.onInitializerExecution(initializerEvent(initializer, false)); + // End of initializer phase + profiler.onInitializersPhase(initializersEvent(false)); + } + + private void sensorPhase(PhasesSumUpTimeProfiler profiler) { + Sensor sensor = new FakeSensor(); + // Start of sensor phase + profiler.onSensorsPhase(sensorsEvent(true)); + // Start of a Sensor + profiler.onSensorExecution(sensorEvent(sensor, true)); + clock.sleep(10); + // End of a Sensor + profiler.onSensorExecution(sensorEvent(sensor, false)); + // End of sensor phase + profiler.onSensorsPhase(sensorsEvent(false)); + } + + private void postJobPhase(PhasesSumUpTimeProfiler profiler) { + PostJob postJob = new FakePostJob(); + // Start of sensor phase + profiler.onPostJobsPhase(postJobsEvent(true)); + // Start of a Sensor + profiler.onPostJobExecution(postJobEvent(postJob, true)); + clock.sleep(30); + // End of a Sensor + profiler.onPostJobExecution(postJobEvent(postJob, false)); + // End of sensor phase + profiler.onPostJobsPhase(postJobsEvent(false)); + } + + private SensorExecutionEvent sensorEvent(final Sensor sensor, final boolean start) { + return new SensorExecutionHandler.SensorExecutionEvent() { + + @Override + public boolean isStart() { + return start; + } + + @Override + public boolean isEnd() { + return !start; + } + + @Override + public Sensor getSensor() { + return sensor; + } + }; + } + + private InitializerExecutionHandler.InitializerExecutionEvent initializerEvent(final Initializer initializer, final boolean start) { + return new InitializerExecutionHandler.InitializerExecutionEvent() { + + @Override + public boolean isStart() { + return start; + } + + @Override + public boolean isEnd() { + return !start; + } + + @Override + public Initializer getInitializer() { + return initializer; + } + }; + } + + private DecoratorExecutionHandler.DecoratorExecutionEvent decoratorEvent(final Decorator decorator, final boolean start) { + return new DecoratorExecutionHandler.DecoratorExecutionEvent() { + + @Override + public boolean isStart() { + return start; + } + + @Override + public boolean isEnd() { + return !start; + } + + @Override + public Decorator getDecorator() { + return decorator; + } + }; + } + + private PostJobExecutionHandler.PostJobExecutionEvent postJobEvent(final PostJob postJob, final boolean start) { + return new PostJobExecutionHandler.PostJobExecutionEvent() { + + @Override + public boolean isStart() { + return start; + } + + @Override + public boolean isEnd() { + return !start; + } + + @Override + public PostJob getPostJob() { + return postJob; + } + }; + } + + private SensorsPhaseEvent sensorsEvent(final boolean start) { + return new SensorsPhaseHandler.SensorsPhaseEvent() { + + @Override + public boolean isStart() { + return start; + } + + @Override + public boolean isEnd() { + return !start; + } + + @Override + public List<Sensor> getSensors() { + return null; + } + }; + } + + private InitializersPhaseHandler.InitializersPhaseEvent initializersEvent(final boolean start) { + return new InitializersPhaseHandler.InitializersPhaseEvent() { + + @Override + public boolean isStart() { + return start; + } + + @Override + public boolean isEnd() { + return !start; + } + + @Override + public List<Initializer> getInitializers() { + return null; + } + }; + } + + private PostJobsPhaseHandler.PostJobsPhaseEvent postJobsEvent(final boolean start) { + return new PostJobsPhaseHandler.PostJobsPhaseEvent() { + + @Override + public boolean isStart() { + return start; + } + + @Override + public boolean isEnd() { + return !start; + } + + @Override + public List<PostJob> getPostJobs() { + return null; + } + }; + } + + private DecoratorsPhaseHandler.DecoratorsPhaseEvent decoratorsEvent(final boolean start) { + return new DecoratorsPhaseHandler.DecoratorsPhaseEvent() { + + @Override + public boolean isStart() { + return start; + } + + @Override + public boolean isEnd() { + return !start; + } + + @Override + public List<Decorator> getDecorators() { + return null; + } + }; + } + + private ProjectAnalysisEvent projectEvent(final Project project, final boolean start) { + return new ProjectAnalysisHandler.ProjectAnalysisEvent() { + @Override + public boolean isStart() { + return start; + } + + @Override + public boolean isEnd() { + return !start; + } + + @Override + public Project getProject() { + return project; + } + }; + } + + public class FakeSensor implements Sensor { + @Override + public void analyse(Project project, SensorContext context) { + } + + @Override + public boolean shouldExecuteOnProject(Project project) { + return true; + } + } + + public class FakeInitializer extends Initializer { + @Override + public void execute(Project project) { + } + + @Override + public boolean shouldExecuteOnProject(Project project) { + return true; + } + } + + public class FakePostJob implements PostJob { + @Override + public void executeOn(Project project, SensorContext context) { + } + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/report/ActiveRulesPublisherTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/report/ActiveRulesPublisherTest.java new file mode 100644 index 00000000000..4b912b22d7b --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/report/ActiveRulesPublisherTest.java @@ -0,0 +1,70 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.report; + +import java.io.File; +import java.util.Arrays; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.sonar.api.batch.rule.ActiveRules; +import org.sonar.api.batch.rule.Severity; +import org.sonar.api.batch.rule.internal.ActiveRulesBuilder; +import org.sonar.api.batch.rule.internal.DefaultActiveRules; +import org.sonar.api.batch.rule.internal.NewActiveRule; +import org.sonar.api.rule.RuleKey; +import org.sonar.core.util.CloseableIterator; +import org.sonar.scanner.protocol.Constants; +import org.sonar.scanner.protocol.output.ScannerReport; +import org.sonar.scanner.protocol.output.ScannerReportReader; +import org.sonar.scanner.protocol.output.ScannerReportWriter; + +import static org.assertj.core.api.Assertions.assertThat; + +public class ActiveRulesPublisherTest { + + @Rule + public TemporaryFolder temp = new TemporaryFolder(); + + @Test + public void write() throws Exception { + File outputDir = temp.newFolder(); + ScannerReportWriter writer = new ScannerReportWriter(outputDir); + + NewActiveRule ar = new ActiveRulesBuilder().create(RuleKey.of("java", "S001")).setSeverity("BLOCKER").setParam("p1", "v1"); + ActiveRules activeRules = new DefaultActiveRules(Arrays.asList(ar)); + + ActiveRulesPublisher underTest = new ActiveRulesPublisher(activeRules); + underTest.publish(writer); + + ScannerReportReader reader = new ScannerReportReader(outputDir); + try (CloseableIterator<ScannerReport.ActiveRule> readIt = reader.readActiveRules()) { + ScannerReport.ActiveRule reportAr = readIt.next(); + assertThat(reportAr.getRuleRepository()).isEqualTo("java"); + assertThat(reportAr.getRuleKey()).isEqualTo("S001"); + assertThat(reportAr.getSeverity()).isEqualTo(Constants.Severity.BLOCKER); + assertThat(reportAr.getParamCount()).isEqualTo(1); + assertThat(reportAr.getParam(0).getKey()).isEqualTo("p1"); + assertThat(reportAr.getParam(0).getValue()).isEqualTo("v1"); + + assertThat(readIt.hasNext()).isFalse(); + } + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/report/AnalysisContextReportPublisherTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/report/AnalysisContextReportPublisherTest.java new file mode 100644 index 00000000000..320941119b3 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/report/AnalysisContextReportPublisherTest.java @@ -0,0 +1,207 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.report; + +import com.google.common.collect.ImmutableMap; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; +import org.apache.commons.io.FileUtils; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.sonar.api.batch.AnalysisMode; +import org.sonar.api.batch.bootstrap.ProjectDefinition; +import org.sonar.api.utils.System2; +import org.sonar.api.utils.log.LogTester; +import org.sonar.api.utils.log.LoggerLevel; +import org.sonar.batch.bootstrap.BatchPluginRepository; +import org.sonar.batch.repository.ProjectRepositories; +import org.sonar.core.platform.PluginInfo; +import org.sonar.scanner.protocol.output.ScannerReportWriter; +import org.sonar.updatecenter.common.Version; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.when; + +public class AnalysisContextReportPublisherTest { + + private static final String BIZ = "BIZ"; + private static final String FOO = "FOO"; + private static final String SONAR_SKIP = "sonar.skip"; + private static final String COM_FOO = "com.foo"; + + @Rule + public LogTester logTester = new LogTester(); + + @Rule + public TemporaryFolder temp = new TemporaryFolder(); + + private BatchPluginRepository pluginRepo = mock(BatchPluginRepository.class); + private AnalysisContextReportPublisher publisher; + private AnalysisMode analysisMode = mock(AnalysisMode.class); + private System2 system2; + private ProjectRepositories projectRepos; + + @Before + public void prepare() throws Exception { + logTester.setLevel(LoggerLevel.INFO); + system2 = mock(System2.class); + when(system2.properties()).thenReturn(new Properties()); + projectRepos = mock(ProjectRepositories.class); + publisher = new AnalysisContextReportPublisher(analysisMode, pluginRepo, system2, projectRepos); + } + + @Test + public void shouldOnlyDumpPluginsByDefault() throws Exception { + when(pluginRepo.getPluginInfos()).thenReturn(Arrays.asList(new PluginInfo("xoo").setName("Xoo").setVersion(Version.create("1.0")))); + + ScannerReportWriter writer = new ScannerReportWriter(temp.newFolder()); + publisher.init(writer); + + assertThat(writer.getFileStructure().analysisLog()).exists(); + assertThat(FileUtils.readFileToString(writer.getFileStructure().analysisLog())).contains("Xoo 1.0 (xoo)"); + + verifyZeroInteractions(system2); + } + + @Test + public void shouldNotDumpInIssuesMode() throws Exception { + when(analysisMode.isIssues()).thenReturn(true); + + ScannerReportWriter writer = new ScannerReportWriter(temp.newFolder()); + publisher.init(writer); + publisher.dumpSettings(ProjectDefinition.create().setProperty("sonar.projectKey", "foo")); + + assertThat(writer.getFileStructure().analysisLog()).doesNotExist(); + } + + @Test + public void dumpServerSideProps() throws Exception { + logTester.setLevel(LoggerLevel.DEBUG); + ScannerReportWriter writer = new ScannerReportWriter(temp.newFolder()); + publisher.init(writer); + + when(projectRepos.moduleExists("foo")).thenReturn(true); + when(projectRepos.settings("foo")).thenReturn(ImmutableMap.of(COM_FOO, "bar", SONAR_SKIP, "true")); + + publisher.dumpSettings(ProjectDefinition.create() + .setProperty("sonar.projectKey", "foo")); + + String content = FileUtils.readFileToString(writer.getFileStructure().analysisLog()); + assertThat(content).doesNotContain(COM_FOO); + assertThat(content).containsOnlyOnce(SONAR_SKIP); + } + + @Test + public void shouldNotDumpSQPropsInSystemProps() throws Exception { + logTester.setLevel(LoggerLevel.DEBUG); + ScannerReportWriter writer = new ScannerReportWriter(temp.newFolder()); + Properties props = new Properties(); + props.setProperty(COM_FOO, "bar"); + props.setProperty(SONAR_SKIP, "true"); + when(system2.properties()).thenReturn(props); + publisher.init(writer); + + String content = FileUtils.readFileToString(writer.getFileStructure().analysisLog()); + assertThat(content).containsOnlyOnce(COM_FOO); + assertThat(content).doesNotContain(SONAR_SKIP); + + publisher.dumpSettings(ProjectDefinition.create() + .setProperty("sonar.projectKey", "foo") + .setProperty(COM_FOO, "bar") + .setProperty(SONAR_SKIP, "true")); + + content = FileUtils.readFileToString(writer.getFileStructure().analysisLog()); + assertThat(content).containsOnlyOnce(COM_FOO); + assertThat(content).containsOnlyOnce(SONAR_SKIP); + } + + @Test + public void shouldNotDumpEnvTwice() throws Exception { + logTester.setLevel(LoggerLevel.DEBUG); + ScannerReportWriter writer = new ScannerReportWriter(temp.newFolder()); + + Map<String, String> env = new HashMap<>(); + env.put(FOO, "BAR"); + env.put(BIZ, "BAZ"); + when(system2.envVariables()).thenReturn(env); + publisher.init(writer); + + String content = FileUtils.readFileToString(writer.getFileStructure().analysisLog()); + assertThat(content).containsOnlyOnce(FOO); + assertThat(content).containsOnlyOnce(BIZ); + assertThat(content).containsSequence(BIZ, FOO); + + publisher.dumpSettings(ProjectDefinition.create() + .setProperty("sonar.projectKey", "foo") + .setProperty("env." + FOO, "BAR")); + + content = FileUtils.readFileToString(writer.getFileStructure().analysisLog()); + assertThat(content).containsOnlyOnce(FOO); + assertThat(content).containsOnlyOnce(BIZ); + assertThat(content).doesNotContain("env." + FOO); + } + + @Test + public void shouldNotDumpSensitiveProperties() throws Exception { + ScannerReportWriter writer = new ScannerReportWriter(temp.newFolder()); + publisher.init(writer); + + assertThat(writer.getFileStructure().analysisLog()).exists(); + + publisher.dumpSettings(ProjectDefinition.create() + .setProperty("sonar.projectKey", "foo") + .setProperty("sonar.projectKey", "foo") + .setProperty("sonar.password", "azerty") + .setProperty("sonar.cpp.license.secured", "AZERTY")); + + assertThat(FileUtils.readFileToString(writer.getFileStructure().analysisLog())).containsSequence( + "sonar.cpp.license.secured=******", + "sonar.password=******", + "sonar.projectKey=foo"); + } + + // SONAR-7371 + @Test + public void dontDumpParentProps() throws Exception { + logTester.setLevel(LoggerLevel.DEBUG); + ScannerReportWriter writer = new ScannerReportWriter(temp.newFolder()); + publisher.init(writer); + + ProjectDefinition module = ProjectDefinition.create() + .setProperty("sonar.projectKey", "foo") + .setProperty(SONAR_SKIP, "true"); + + ProjectDefinition.create() + .setProperty("sonar.projectKey", "parent") + .setProperty(SONAR_SKIP, "true") + .addSubProject(module); + + publisher.dumpSettings(module); + + String content = FileUtils.readFileToString(writer.getFileStructure().analysisLog()); + assertThat(content).doesNotContain(SONAR_SKIP); + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/report/ComponentsPublisherTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/report/ComponentsPublisherTest.java new file mode 100644 index 00000000000..0a4ed8d69cd --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/report/ComponentsPublisherTest.java @@ -0,0 +1,170 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.report; + +import java.io.File; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.sonar.api.CoreProperties; +import org.sonar.api.batch.bootstrap.ProjectDefinition; +import org.sonar.api.batch.fs.internal.DefaultInputDir; +import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.fs.internal.DefaultInputModule; +import org.sonar.api.resources.Directory; +import org.sonar.api.resources.Java; +import org.sonar.api.resources.Project; +import org.sonar.api.utils.DateUtils; +import org.sonar.batch.index.BatchComponentCache; +import org.sonar.batch.scan.ImmutableProjectReactor; +import org.sonar.scanner.protocol.Constants.ComponentLinkType; +import org.sonar.scanner.protocol.output.ScannerReportReader; +import org.sonar.scanner.protocol.output.ScannerReportWriter; +import org.sonar.scanner.protocol.output.FileStructure; +import org.sonar.scanner.protocol.output.ScannerReport.Component; + +import static org.assertj.core.api.Assertions.assertThat; + +public class ComponentsPublisherTest { + + @Rule + public TemporaryFolder temp = new TemporaryFolder(); + + BatchComponentCache resourceCache = new BatchComponentCache(); + + @Test + public void add_components_to_report() throws Exception { + + ProjectDefinition rootDef = ProjectDefinition.create().setKey("foo"); + rootDef.properties().put(CoreProperties.PROJECT_VERSION_PROPERTY, "1.0"); + Project root = new Project("foo").setName("Root project").setDescription("Root description") + .setAnalysisDate(DateUtils.parseDate(("2012-12-12"))); + root.setId(1).setUuid("PROJECT_UUID"); + resourceCache.add(root, null).setInputComponent(new DefaultInputModule("foo")); + + Project module1 = new Project("module1").setName("Module1").setDescription("Module description"); + module1.setParent(root); + module1.setId(2).setUuid("MODULE_UUID"); + resourceCache.add(module1, root).setInputComponent(new DefaultInputModule("module1")); + rootDef.addSubProject(ProjectDefinition.create().setKey("module1")); + + Directory dir = Directory.create("src"); + dir.setEffectiveKey("module1:src"); + dir.setId(3).setUuid("DIR_UUID"); + resourceCache.add(dir, module1).setInputComponent(new DefaultInputDir("foo", "src")); + + org.sonar.api.resources.File file = org.sonar.api.resources.File.create("src/Foo.java", Java.INSTANCE, false); + file.setEffectiveKey("module1:src/Foo.java"); + file.setId(4).setUuid("FILE_UUID"); + resourceCache.add(file, dir).setInputComponent(new DefaultInputFile("module1", "src/Foo.java").setLines(2)); + + org.sonar.api.resources.File fileWithoutLang = org.sonar.api.resources.File.create("src/make", null, false); + fileWithoutLang.setEffectiveKey("module1:src/make"); + fileWithoutLang.setId(5).setUuid("FILE_WITHOUT_LANG_UUID"); + resourceCache.add(fileWithoutLang, dir).setInputComponent(new DefaultInputFile("module1", "src/make").setLines(10)); + + org.sonar.api.resources.File testFile = org.sonar.api.resources.File.create("test/FooTest.java", Java.INSTANCE, true); + testFile.setEffectiveKey("module1:test/FooTest.java"); + testFile.setId(6).setUuid("TEST_FILE_UUID"); + resourceCache.add(testFile, dir).setInputComponent(new DefaultInputFile("module1", "test/FooTest.java").setLines(4)); + + ImmutableProjectReactor reactor = new ImmutableProjectReactor(rootDef); + + ComponentsPublisher publisher = new ComponentsPublisher(reactor, resourceCache); + + File outputDir = temp.newFolder(); + ScannerReportWriter writer = new ScannerReportWriter(outputDir); + publisher.publish(writer); + + assertThat(writer.hasComponentData(FileStructure.Domain.COMPONENT, 1)).isTrue(); + assertThat(writer.hasComponentData(FileStructure.Domain.COMPONENT, 2)).isTrue(); + assertThat(writer.hasComponentData(FileStructure.Domain.COMPONENT, 3)).isTrue(); + assertThat(writer.hasComponentData(FileStructure.Domain.COMPONENT, 4)).isTrue(); + assertThat(writer.hasComponentData(FileStructure.Domain.COMPONENT, 5)).isTrue(); + assertThat(writer.hasComponentData(FileStructure.Domain.COMPONENT, 6)).isTrue(); + + // no such reference + assertThat(writer.hasComponentData(FileStructure.Domain.COMPONENT, 7)).isFalse(); + + ScannerReportReader reader = new ScannerReportReader(outputDir); + Component rootProtobuf = reader.readComponent(1); + assertThat(rootProtobuf.getKey()).isEqualTo("foo"); + assertThat(rootProtobuf.getDescription()).isEqualTo("Root description"); + assertThat(rootProtobuf.getVersion()).isEqualTo("1.0"); + assertThat(rootProtobuf.getLinkCount()).isEqualTo(0); + + Component module1Protobuf = reader.readComponent(2); + assertThat(module1Protobuf.getKey()).isEqualTo("module1"); + assertThat(module1Protobuf.getDescription()).isEqualTo("Module description"); + assertThat(module1Protobuf.getVersion()).isEqualTo("1.0"); + } + + @Test + public void add_components_with_links_and_branch() throws Exception { + // inputs + ProjectDefinition rootDef = ProjectDefinition.create().setKey("foo"); + rootDef.properties().put(CoreProperties.PROJECT_VERSION_PROPERTY, "1.0"); + Project root = new Project("foo:my_branch").setName("Root project") + .setAnalysisDate(DateUtils.parseDate(("2012-12-12"))); + root.setId(1).setUuid("PROJECT_UUID"); + resourceCache.add(root, null).setInputComponent(new DefaultInputModule("foo")); + rootDef.properties().put(CoreProperties.LINKS_HOME_PAGE, "http://home"); + rootDef.properties().put(CoreProperties.PROJECT_BRANCH_PROPERTY, "my_branch"); + + Project module1 = new Project("module1:my_branch").setName("Module1"); + module1.setParent(root); + module1.setId(2).setUuid("MODULE_UUID"); + resourceCache.add(module1, root).setInputComponent(new DefaultInputModule("module1")); + ProjectDefinition moduleDef = ProjectDefinition.create().setKey("module1"); + moduleDef.properties().put(CoreProperties.LINKS_CI, "http://ci"); + rootDef.addSubProject(moduleDef); + + Directory dir = Directory.create("src"); + dir.setEffectiveKey("module1:my_branch:my_branch:src"); + dir.setId(3).setUuid("DIR_UUID"); + resourceCache.add(dir, module1).setInputComponent(new DefaultInputDir("foo", "src")); + + org.sonar.api.resources.File file = org.sonar.api.resources.File.create("src/Foo.java", Java.INSTANCE, false); + file.setEffectiveKey("module1:my_branch:my_branch:src/Foo.java"); + file.setId(4).setUuid("FILE_UUID"); + resourceCache.add(file, dir).setInputComponent(new DefaultInputFile("module1", "src/Foo.java").setLines(2)); + + ImmutableProjectReactor reactor = new ImmutableProjectReactor(rootDef); + + ComponentsPublisher publisher = new ComponentsPublisher(reactor, resourceCache); + + File outputDir = temp.newFolder(); + ScannerReportWriter writer = new ScannerReportWriter(outputDir); + publisher.publish(writer); + + ScannerReportReader reader = new ScannerReportReader(outputDir); + Component rootProtobuf = reader.readComponent(1); + assertThat(rootProtobuf.getVersion()).isEqualTo("1.0"); + assertThat(rootProtobuf.getLinkCount()).isEqualTo(1); + assertThat(rootProtobuf.getLink(0).getType()).isEqualTo(ComponentLinkType.HOME); + assertThat(rootProtobuf.getLink(0).getHref()).isEqualTo("http://home"); + + Component module1Protobuf = reader.readComponent(2); + assertThat(module1Protobuf.getVersion()).isEqualTo("1.0"); + assertThat(module1Protobuf.getLinkCount()).isEqualTo(1); + assertThat(module1Protobuf.getLink(0).getType()).isEqualTo(ComponentLinkType.CI); + assertThat(module1Protobuf.getLink(0).getHref()).isEqualTo("http://ci"); + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/report/CoveragePublisherTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/report/CoveragePublisherTest.java new file mode 100644 index 00000000000..72c31cf4928 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/report/CoveragePublisherTest.java @@ -0,0 +1,116 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.report; + +import java.io.File; +import java.util.Date; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.fs.internal.DefaultInputModule; +import org.sonar.api.measures.CoreMetrics; +import org.sonar.api.measures.Measure; +import org.sonar.api.resources.Project; +import org.sonar.batch.index.BatchComponentCache; +import org.sonar.batch.scan.measure.MeasureCache; +import org.sonar.core.util.CloseableIterator; +import org.sonar.scanner.protocol.output.ScannerReportReader; +import org.sonar.scanner.protocol.output.ScannerReportWriter; +import org.sonar.scanner.protocol.output.ScannerReport.Coverage; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class CoveragePublisherTest { + + @Rule + public TemporaryFolder temp = new TemporaryFolder(); + + private MeasureCache measureCache; + private CoveragePublisher publisher; + + private org.sonar.api.resources.Resource sampleFile; + + @Before + public void prepare() { + Project p = new Project("foo").setAnalysisDate(new Date(1234567L)); + BatchComponentCache resourceCache = new BatchComponentCache(); + sampleFile = org.sonar.api.resources.File.create("src/Foo.php").setEffectiveKey("foo:src/Foo.php"); + resourceCache.add(p, null).setInputComponent(new DefaultInputModule("foo")); + resourceCache.add(sampleFile, null).setInputComponent(new DefaultInputFile("foo", "src/Foo.php").setLines(5)); + measureCache = mock(MeasureCache.class); + when(measureCache.byMetric(anyString(), anyString())).thenReturn(null); + publisher = new CoveragePublisher(resourceCache, measureCache); + } + + @Test + public void publishCoverage() throws Exception { + + Measure utLineHits = new Measure<>(CoreMetrics.COVERAGE_LINE_HITS_DATA).setData("2=1;3=1;5=0;6=3"); + when(measureCache.byMetric("foo:src/Foo.php", CoreMetrics.COVERAGE_LINE_HITS_DATA_KEY)).thenReturn(utLineHits); + + Measure conditionsByLine = new Measure<>(CoreMetrics.CONDITIONS_BY_LINE).setData("3=4"); + when(measureCache.byMetric("foo:src/Foo.php", CoreMetrics.CONDITIONS_BY_LINE_KEY)).thenReturn(conditionsByLine); + + Measure coveredConditionsByUts = new Measure<>(CoreMetrics.COVERED_CONDITIONS_BY_LINE).setData("3=2"); + when(measureCache.byMetric("foo:src/Foo.php", CoreMetrics.COVERED_CONDITIONS_BY_LINE_KEY)).thenReturn(coveredConditionsByUts); + + Measure itLineHits = new Measure<>(CoreMetrics.IT_COVERAGE_LINE_HITS_DATA).setData("2=0;3=0;5=1"); + when(measureCache.byMetric("foo:src/Foo.php", CoreMetrics.IT_COVERAGE_LINE_HITS_DATA_KEY)).thenReturn(itLineHits); + + Measure coveredConditionsByIts = new Measure<>(CoreMetrics.IT_COVERED_CONDITIONS_BY_LINE).setData("3=1"); + when(measureCache.byMetric("foo:src/Foo.php", CoreMetrics.IT_COVERED_CONDITIONS_BY_LINE_KEY)).thenReturn(coveredConditionsByIts); + + Measure overallCoveredConditions = new Measure<>(CoreMetrics.OVERALL_COVERED_CONDITIONS_BY_LINE).setData("3=2"); + when(measureCache.byMetric("foo:src/Foo.php", CoreMetrics.OVERALL_COVERED_CONDITIONS_BY_LINE_KEY)).thenReturn(overallCoveredConditions); + + File outputDir = temp.newFolder(); + ScannerReportWriter writer = new ScannerReportWriter(outputDir); + + publisher.publish(writer); + + try (CloseableIterator<Coverage> it = new ScannerReportReader(outputDir).readComponentCoverage(2)) { + assertThat(it.next()).isEqualTo(Coverage.newBuilder() + .setLine(2) + .setUtHits(true) + .setItHits(false) + .build()); + assertThat(it.next()).isEqualTo(Coverage.newBuilder() + .setLine(3) + .setUtHits(true) + .setItHits(false) + .setConditions(4) + .setUtCoveredConditions(2) + .setItCoveredConditions(1) + .setOverallCoveredConditions(2) + .build()); + assertThat(it.next()).isEqualTo(Coverage.newBuilder() + .setLine(5) + .setUtHits(false) + .setItHits(true) + .build()); + } + + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/report/MeasuresPublisherTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/report/MeasuresPublisherTest.java new file mode 100644 index 00000000000..0c7a29dd377 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/report/MeasuresPublisherTest.java @@ -0,0 +1,113 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.report; + +import java.io.File; +import java.util.Collections; +import java.util.Date; +import org.apache.commons.lang.exception.ExceptionUtils; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.rules.TemporaryFolder; +import org.sonar.api.measures.CoreMetrics; +import org.sonar.api.measures.Measure; +import org.sonar.api.resources.Project; +import org.sonar.api.resources.Resource; +import org.sonar.batch.index.BatchComponentCache; +import org.sonar.batch.scan.measure.MeasureCache; +import org.sonar.core.metric.BatchMetrics; +import org.sonar.core.util.CloseableIterator; +import org.sonar.scanner.protocol.output.ScannerReport; +import org.sonar.scanner.protocol.output.ScannerReportReader; +import org.sonar.scanner.protocol.output.ScannerReportWriter; + +import static java.util.Arrays.asList; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.fail; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class MeasuresPublisherTest { + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Rule + public TemporaryFolder temp = new TemporaryFolder(); + + private MeasureCache measureCache; + private MeasuresPublisher publisher; + + private org.sonar.api.resources.Resource sampleFile; + + @Before + public void prepare() { + Project p = new Project("foo").setAnalysisDate(new Date(1234567L)); + BatchComponentCache resourceCache = new BatchComponentCache(); + sampleFile = org.sonar.api.resources.File.create("src/Foo.php").setEffectiveKey("foo:src/Foo.php"); + resourceCache.add(p, null); + resourceCache.add(sampleFile, null); + measureCache = mock(MeasureCache.class); + when(measureCache.byResource(any(Resource.class))).thenReturn(Collections.<Measure>emptyList()); + publisher = new MeasuresPublisher(resourceCache, measureCache, new BatchMetrics()); + } + + @Test + public void publishMeasures() throws Exception { + Measure measure = new Measure<>(CoreMetrics.LINES_TO_COVER) + .setValue(2.0); + // String value + Measure stringMeasure = new Measure<>(CoreMetrics.NCLOC_LANGUAGE_DISTRIBUTION) + .setData("foo bar"); + when(measureCache.byResource(sampleFile)).thenReturn(asList(measure, stringMeasure)); + + File outputDir = temp.newFolder(); + ScannerReportWriter writer = new ScannerReportWriter(outputDir); + + publisher.publish(writer); + + ScannerReportReader reader = new ScannerReportReader(outputDir); + + assertThat(reader.readComponentMeasures(1)).hasSize(0); + try (CloseableIterator<ScannerReport.Measure> componentMeasures = reader.readComponentMeasures(2)) { + assertThat(componentMeasures).hasSize(2); + } + } + + @Test + public void fail_with_IAE_when_measure_has_no_value() throws Exception { + Measure measure = new Measure<>(CoreMetrics.LINES_TO_COVER); + when(measureCache.byResource(sampleFile)).thenReturn(Collections.singletonList(measure)); + + File outputDir = temp.newFolder(); + ScannerReportWriter writer = new ScannerReportWriter(outputDir); + + try { + publisher.publish(writer); + fail(); + } catch (RuntimeException e) { + assertThat(ExceptionUtils.getFullStackTrace(e)).contains("Measure on metric 'lines_to_cover' and component 'foo:src/Foo.php' has no value, but it's not allowed"); + } + } + +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/report/MetadataPublisherTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/report/MetadataPublisherTest.java new file mode 100644 index 00000000000..7069845e9cf --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/report/MetadataPublisherTest.java @@ -0,0 +1,100 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.report; + +import java.io.File; +import java.util.Date; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.sonar.api.CoreProperties; +import org.sonar.api.batch.bootstrap.ProjectDefinition; +import org.sonar.api.config.Settings; +import org.sonar.api.resources.Project; +import org.sonar.batch.index.BatchComponentCache; +import org.sonar.batch.scan.ImmutableProjectReactor; +import org.sonar.scanner.protocol.output.ScannerReport; +import org.sonar.scanner.protocol.output.ScannerReportReader; +import org.sonar.scanner.protocol.output.ScannerReportWriter; + +import static org.assertj.core.api.Assertions.assertThat; + +public class MetadataPublisherTest { + + @Rule + public TemporaryFolder temp = new TemporaryFolder(); + + private ProjectDefinition projectDef; + private Project project; + private MetadataPublisher underTest; + private Settings settings; + + @Before + public void prepare() { + projectDef = ProjectDefinition.create().setKey("foo"); + project = new Project("foo").setAnalysisDate(new Date(1234567L)); + BatchComponentCache componentCache = new BatchComponentCache(); + org.sonar.api.resources.Resource sampleFile = org.sonar.api.resources.File.create("src/Foo.php").setEffectiveKey("foo:src/Foo.php"); + componentCache.add(project, null); + componentCache.add(sampleFile, project); + settings = new Settings(); + underTest = new MetadataPublisher(componentCache, new ImmutableProjectReactor(projectDef), settings); + } + + @Test + public void write_metadata() throws Exception { + settings.setProperty(CoreProperties.CPD_CROSS_PROJECT, "true"); + File outputDir = temp.newFolder(); + ScannerReportWriter writer = new ScannerReportWriter(outputDir); + + underTest.publish(writer); + + ScannerReportReader reader = new ScannerReportReader(outputDir); + ScannerReport.Metadata metadata = reader.readMetadata(); + assertThat(metadata.getAnalysisDate()).isEqualTo(1234567L); + assertThat(metadata.getProjectKey()).isEqualTo("foo"); + assertThat(metadata.getProjectKey()).isEqualTo("foo"); + assertThat(metadata.getCrossProjectDuplicationActivated()).isTrue(); + } + + @Test + public void write_project_branch() throws Exception { + settings.setProperty(CoreProperties.CPD_CROSS_PROJECT, "true"); + settings.setProperty(CoreProperties.PROJECT_BRANCH_PROPERTY, "myBranch"); + projectDef.properties().put(CoreProperties.PROJECT_BRANCH_PROPERTY, "myBranch"); + project.setKey("foo:myBranch"); + project.setEffectiveKey("foo:myBranch"); + + File outputDir = temp.newFolder(); + ScannerReportWriter writer = new ScannerReportWriter(outputDir); + + underTest.publish(writer); + + ScannerReportReader reader = new ScannerReportReader(outputDir); + ScannerReport.Metadata metadata = reader.readMetadata(); + assertThat(metadata.getAnalysisDate()).isEqualTo(1234567L); + assertThat(metadata.getProjectKey()).isEqualTo("foo"); + assertThat(metadata.getBranch()).isEqualTo("myBranch"); + // Cross project duplication disabled on branches + assertThat(metadata.getCrossProjectDuplicationActivated()).isFalse(); + } + +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/report/ReportPublisherTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/report/ReportPublisherTest.java new file mode 100644 index 00000000000..bb54bfb9113 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/report/ReportPublisherTest.java @@ -0,0 +1,164 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.report; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.rules.TemporaryFolder; +import org.mockito.Mockito; +import org.sonar.api.CoreProperties; +import org.sonar.api.batch.bootstrap.ProjectDefinition; +import org.sonar.api.config.PropertyDefinitions; +import org.sonar.api.config.Settings; +import org.sonar.api.utils.MessageException; +import org.sonar.api.utils.TempFolder; +import org.sonar.api.utils.log.LogTester; +import org.sonar.api.utils.log.LoggerLevel; +import org.sonar.batch.analysis.DefaultAnalysisMode; +import org.sonar.batch.bootstrap.BatchWsClient; +import org.sonar.batch.scan.ImmutableProjectReactor; +import org.sonar.core.config.CorePropertyDefinitions; + +import static org.apache.commons.io.FileUtils.readFileToString; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class ReportPublisherTest { + + @Rule + public LogTester logTester = new LogTester(); + + @Rule + public TemporaryFolder temp = new TemporaryFolder(); + + @Rule + public ExpectedException exception = ExpectedException.none(); + + DefaultAnalysisMode mode = mock(DefaultAnalysisMode.class); + Settings settings = new Settings(new PropertyDefinitions(CorePropertyDefinitions.all())); + BatchWsClient wsClient = mock(BatchWsClient.class, Mockito.RETURNS_DEEP_STUBS); + ImmutableProjectReactor reactor = mock(ImmutableProjectReactor.class); + ProjectDefinition root; + AnalysisContextReportPublisher contextPublisher = mock(AnalysisContextReportPublisher.class); + + @Before + public void setUp() { + root = ProjectDefinition.create().setKey("struts").setWorkDir(temp.getRoot()); + when(reactor.getRoot()).thenReturn(root); + when(wsClient.baseUrl()).thenReturn("https://localhost/"); + } + + @Test + public void log_and_dump_information_about_report_uploading() throws IOException { + ReportPublisher underTest = new ReportPublisher(settings, wsClient, contextPublisher, reactor, mode, mock(TempFolder.class), new ReportPublisherStep[0]); + + underTest.logSuccess("TASK-123"); + + assertThat(logTester.logs(LoggerLevel.INFO)) + .contains("ANALYSIS SUCCESSFUL, you can browse https://localhost/dashboard/index/struts") + .contains("Note that you will be able to access the updated dashboard once the server has processed the submitted analysis report") + .contains("More about the report processing at https://localhost/api/ce/task?id=TASK-123"); + + File detailsFile = new File(temp.getRoot(), "report-task.txt"); + assertThat(readFileToString(detailsFile)).isEqualTo( + "projectKey=struts\n" + + "serverUrl=https://localhost\n" + + "dashboardUrl=https://localhost/dashboard/index/struts\n" + + "ceTaskId=TASK-123\n" + + "ceTaskUrl=https://localhost/api/ce/task?id=TASK-123\n" + ); + } + + @Test + public void log_public_url_if_defined() throws IOException { + settings.setProperty(CoreProperties.SERVER_BASE_URL, "https://publicserver/sonarqube"); + ReportPublisher underTest = new ReportPublisher(settings, wsClient, contextPublisher, reactor, mode, mock(TempFolder.class), new ReportPublisherStep[0]); + + underTest.logSuccess("TASK-123"); + + assertThat(logTester.logs(LoggerLevel.INFO)) + .contains("ANALYSIS SUCCESSFUL, you can browse https://publicserver/sonarqube/dashboard/index/struts") + .contains("More about the report processing at https://publicserver/sonarqube/api/ce/task?id=TASK-123"); + + File detailsFile = new File(temp.getRoot(), "report-task.txt"); + assertThat(readFileToString(detailsFile)).isEqualTo( + "projectKey=struts\n" + + "serverUrl=https://publicserver/sonarqube\n" + + "dashboardUrl=https://publicserver/sonarqube/dashboard/index/struts\n" + + "ceTaskId=TASK-123\n" + + "ceTaskUrl=https://publicserver/sonarqube/api/ce/task?id=TASK-123\n" + ); + } + + @Test + public void fail_if_public_url_malformed() throws IOException { + settings.setProperty(CoreProperties.SERVER_BASE_URL, "invalid"); + ReportPublisher underTest = new ReportPublisher(settings, wsClient, contextPublisher, reactor, mode, mock(TempFolder.class), new ReportPublisherStep[0]); + + exception.expect(MessageException.class); + exception.expectMessage("Failed to parse public URL set in SonarQube server: invalid"); + underTest.start(); + } + + @Test + public void log_but_not_dump_information_when_report_is_not_uploaded() { + ReportPublisher underTest = new ReportPublisher(settings, wsClient, contextPublisher, reactor, mode, mock(TempFolder.class), new ReportPublisherStep[0]); + + underTest.logSuccess(/* report not uploaded, no server task */null); + + assertThat(logTester.logs(LoggerLevel.INFO)) + .contains("ANALYSIS SUCCESSFUL") + .doesNotContain("dashboard/index"); + + File detailsFile = new File(temp.getRoot(), ReportPublisher.METADATA_DUMP_FILENAME); + assertThat(detailsFile).doesNotExist(); + } + + @Test + public void should_not_delete_report_if_property_is_set() throws IOException { + settings.setProperty("sonar.batch.keepReport", true); + Path reportDir = temp.getRoot().toPath().resolve("batch-report"); + Files.createDirectory(reportDir); + ReportPublisher underTest = new ReportPublisher(settings, wsClient, contextPublisher, reactor, mode, mock(TempFolder.class), new ReportPublisherStep[0]); + + underTest.start(); + underTest.stop(); + assertThat(reportDir).isDirectory(); + } + + @Test + public void should_delete_report_by_default() throws IOException { + Path reportDir = temp.getRoot().toPath().resolve("batch-report"); + Files.createDirectory(reportDir); + ReportPublisher job = new ReportPublisher(settings, wsClient, contextPublisher, reactor, mode, mock(TempFolder.class), new ReportPublisherStep[0]); + + job.start(); + job.stop(); + assertThat(reportDir).doesNotExist(); + } + +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/report/SourcePublisherTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/report/SourcePublisherTest.java new file mode 100644 index 00000000000..683d6fecb8a --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/report/SourcePublisherTest.java @@ -0,0 +1,119 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.report; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Date; +import org.apache.commons.io.FileUtils; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.fs.internal.DefaultInputModule; +import org.sonar.api.resources.Project; +import org.sonar.api.resources.Qualifiers; +import org.sonar.batch.index.BatchComponentCache; +import org.sonar.scanner.protocol.output.ScannerReportWriter; + +import static org.assertj.core.api.Assertions.assertThat; + +public class SourcePublisherTest { + + @Rule + public TemporaryFolder temp = new TemporaryFolder(); + + private SourcePublisher publisher; + + private File sourceFile; + + private ScannerReportWriter writer; + + private org.sonar.api.resources.File sampleFile; + + @Before + public void prepare() throws IOException { + Project p = new Project("foo").setAnalysisDate(new Date(1234567L)); + BatchComponentCache resourceCache = new BatchComponentCache(); + sampleFile = org.sonar.api.resources.File.create("src/Foo.php"); + sampleFile.setEffectiveKey("foo:src/Foo.php"); + resourceCache.add(p, null).setInputComponent(new DefaultInputModule("foo")); + File baseDir = temp.newFolder(); + sourceFile = new File(baseDir, "src/Foo.php"); + resourceCache.add(sampleFile, null).setInputComponent( + new DefaultInputFile("foo", "src/Foo.php").setLines(5).setModuleBaseDir(baseDir.toPath()).setCharset(StandardCharsets.ISO_8859_1)); + publisher = new SourcePublisher(resourceCache); + File outputDir = temp.newFolder(); + writer = new ScannerReportWriter(outputDir); + } + + @Test + public void publishEmptySource() throws Exception { + FileUtils.write(sourceFile, "", StandardCharsets.ISO_8859_1); + + publisher.publish(writer); + + File out = writer.getSourceFile(2); + assertThat(FileUtils.readFileToString(out, StandardCharsets.UTF_8)).isEqualTo(""); + } + + @Test + public void publishSourceWithLastEmptyLine() throws Exception { + FileUtils.write(sourceFile, "1\n2\n3\n4\n", StandardCharsets.ISO_8859_1); + + publisher.publish(writer); + + File out = writer.getSourceFile(2); + assertThat(FileUtils.readFileToString(out, StandardCharsets.UTF_8)).isEqualTo("1\n2\n3\n4\n"); + } + + @Test + public void publishTestSource() throws Exception { + FileUtils.write(sourceFile, "1\n2\n3\n4\n", StandardCharsets.ISO_8859_1); + sampleFile.setQualifier(Qualifiers.UNIT_TEST_FILE); + + publisher.publish(writer); + + File out = writer.getSourceFile(2); + assertThat(FileUtils.readFileToString(out, StandardCharsets.UTF_8)).isEqualTo("1\n2\n3\n4\n"); + } + + @Test + public void publishSourceWithLastLineNotEmpty() throws Exception { + FileUtils.write(sourceFile, "1\n2\n3\n4\n5", StandardCharsets.ISO_8859_1); + + publisher.publish(writer); + + File out = writer.getSourceFile(2); + assertThat(FileUtils.readFileToString(out, StandardCharsets.UTF_8)).isEqualTo("1\n2\n3\n4\n5"); + } + + @Test + public void cleanLineEnds() throws Exception { + FileUtils.write(sourceFile, "\n2\r\n3\n4\r5", StandardCharsets.ISO_8859_1); + + publisher.publish(writer); + + File out = writer.getSourceFile(2); + assertThat(FileUtils.readFileToString(out, StandardCharsets.UTF_8)).isEqualTo("\n2\n3\n4\n5"); + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/repository/DefaultGlobalRepositoriesLoaderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/repository/DefaultGlobalRepositoriesLoaderTest.java new file mode 100644 index 00000000000..cb3781e5103 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/repository/DefaultGlobalRepositoriesLoaderTest.java @@ -0,0 +1,78 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.repository; + +import org.apache.commons.lang.mutable.MutableBoolean; +import org.junit.Before; +import org.junit.Test; +import org.sonar.batch.cache.WSLoader; +import org.sonar.batch.cache.WSLoaderResult; +import org.sonar.scanner.protocol.input.GlobalRepositories; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +public class DefaultGlobalRepositoriesLoaderTest { + private static final String BATCH_GLOBAL_URL = "/batch/global"; + private WSLoader wsLoader; + private WSLoaderResult<String> result; + private DefaultGlobalRepositoriesLoader globalRepositoryLoader; + + @Before + public void setUp() { + wsLoader = mock(WSLoader.class); + result = new WSLoaderResult<>(new GlobalRepositories().toJson(), true); + when(wsLoader.loadString(BATCH_GLOBAL_URL)).thenReturn(result); + + globalRepositoryLoader = new DefaultGlobalRepositoriesLoader(wsLoader); + } + + @Test + public void test() { + MutableBoolean fromCache = new MutableBoolean(); + globalRepositoryLoader.load(fromCache); + + assertThat(fromCache.booleanValue()).isTrue(); + verify(wsLoader).loadString(BATCH_GLOBAL_URL); + verifyNoMoreInteractions(wsLoader); + } + + @Test + public void testFromServer() { + result = new WSLoaderResult<>(new GlobalRepositories().toJson(), false); + when(wsLoader.loadString(BATCH_GLOBAL_URL)).thenReturn(result); + MutableBoolean fromCache = new MutableBoolean(); + globalRepositoryLoader.load(fromCache); + + assertThat(fromCache.booleanValue()).isFalse(); + verify(wsLoader).loadString(BATCH_GLOBAL_URL); + verifyNoMoreInteractions(wsLoader); + } + + public void testWithoutArg() { + globalRepositoryLoader.load(null); + + verify(wsLoader).loadString(BATCH_GLOBAL_URL); + verifyNoMoreInteractions(wsLoader); + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/repository/DefaultProjectRepositoriesLoaderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/repository/DefaultProjectRepositoriesLoaderTest.java new file mode 100644 index 00000000000..d633820ca83 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/repository/DefaultProjectRepositoriesLoaderTest.java @@ -0,0 +1,144 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.repository; + +import com.google.common.io.Resources; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import org.apache.commons.lang.mutable.MutableBoolean; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.sonar.api.utils.MessageException; +import org.sonar.batch.cache.WSLoader; +import org.sonar.batch.cache.WSLoaderResult; +import org.sonarqube.ws.WsBatch.WsProjectResponse; +import org.sonarqube.ws.client.HttpException; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class DefaultProjectRepositoriesLoaderTest { + private final static String PROJECT_KEY = "foo?"; + @Rule + public ExpectedException thrown = ExpectedException.none(); + + private DefaultProjectRepositoriesLoader loader; + private WSLoader wsLoader; + + @Before + public void prepare() throws IOException { + wsLoader = mock(WSLoader.class); + InputStream is = mockData(); + when(wsLoader.loadStream(anyString())).thenReturn(new WSLoaderResult<>(is, true)); + loader = new DefaultProjectRepositoriesLoader(wsLoader); + } + + @Test + public void continueOnError() { + when(wsLoader.loadStream(anyString())).thenThrow(IllegalStateException.class); + ProjectRepositories proj = loader.load(PROJECT_KEY, false, null); + assertThat(proj.exists()).isEqualTo(false); + } + + @Test + public void parsingError() throws IOException { + InputStream is = mock(InputStream.class); + when(is.read()).thenThrow(IOException.class); + + when(wsLoader.loadStream(anyString())).thenReturn(new WSLoaderResult<>(is, false)); + loader.load(PROJECT_KEY, false, null); + } + + @Test(expected = IllegalStateException.class) + public void failFastHttpError() { + HttpException http = new HttpException("url", 403); + IllegalStateException e = new IllegalStateException("http error", http); + when(wsLoader.loadStream(anyString())).thenThrow(e); + loader.load(PROJECT_KEY, false, null); + } + + @Test + public void failFastHttpErrorMessageException() { + thrown.expect(MessageException.class); + thrown.expectMessage("http error"); + + HttpException http = new HttpException("uri", 403); + MessageException e = MessageException.of("http error", http); + when(wsLoader.loadStream(anyString())).thenThrow(e); + loader.load(PROJECT_KEY, false, null); + } + + @Test + public void passIssuesModeParameter() { + loader.load(PROJECT_KEY, false, null); + verify(wsLoader).loadStream("/batch/project.protobuf?key=foo%3F"); + + loader.load(PROJECT_KEY, true, null); + verify(wsLoader).loadStream("/batch/project.protobuf?key=foo%3F&issues_mode=true"); + } + + @Test + public void deserializeResponse() throws IOException { + MutableBoolean fromCache = new MutableBoolean(); + loader.load(PROJECT_KEY, false, fromCache); + assertThat(fromCache.booleanValue()).isTrue(); + } + + @Test + public void passAndEncodeProjectKeyParameter() { + loader.load(PROJECT_KEY, false, null); + verify(wsLoader).loadStream("/batch/project.protobuf?key=foo%3F"); + } + + private InputStream mockData() throws IOException { + ByteArrayOutputStream os = new ByteArrayOutputStream(); + WsProjectResponse.Builder projectResponseBuilder = WsProjectResponse.newBuilder(); + WsProjectResponse response = projectResponseBuilder.build(); + response.writeTo(os); + + return new ByteArrayInputStream(os.toByteArray()); + } + + @Test + public void readRealResponse() throws IOException { + InputStream is = getTestResource("project.protobuf"); + when(wsLoader.loadStream(anyString())).thenReturn(new WSLoaderResult<>(is, true)); + + ProjectRepositories proj = loader.load("org.sonarsource.github:sonar-github-plugin", true, null); + FileData fd = proj.fileData("org.sonarsource.github:sonar-github-plugin", + "src/test/java/org/sonar/plugins/github/PullRequestIssuePostJobTest.java"); + + assertThat(fd.revision()).isEqualTo("27bf2c54633d05c5df402bbe09471fe43bd9e2e5"); + assertThat(fd.hash()).isEqualTo("edb6b3b9ab92d8dc53ba90ab86cd422e"); + } + + private InputStream getTestResource(String name) throws IOException { + return Resources.asByteSource(this.getClass().getResource(this.getClass().getSimpleName() + "/" + name)) + .openBufferedStream(); + } + +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/repository/DefaultQualityProfileLoaderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/repository/DefaultQualityProfileLoaderTest.java new file mode 100644 index 00000000000..d3b4ce6b87d --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/repository/DefaultQualityProfileLoaderTest.java @@ -0,0 +1,116 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.repository; + +import org.sonar.api.utils.MessageException; + +import org.sonarqube.ws.QualityProfiles; +import com.google.common.io.Resources; +import org.sonarqube.ws.QualityProfiles.SearchWsResponse.QualityProfile; +import org.sonar.batch.cache.WSLoaderResult; +import org.sonar.batch.cache.WSLoader; +import org.junit.Rule; +import org.junit.rules.ExpectedException; +import org.junit.Before; +import org.junit.Test; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class DefaultQualityProfileLoaderTest { + @Rule + public ExpectedException exception = ExpectedException.none(); + + private DefaultQualityProfileLoader qpLoader; + private WSLoader ws; + private InputStream is; + + @Before + public void setUp() throws IOException { + ws = mock(WSLoader.class); + is = mock(InputStream.class); + when(is.read()).thenReturn(-1); + WSLoaderResult<InputStream> result = new WSLoaderResult<>(is, false); + when(ws.loadStream(anyString())).thenReturn(result); + qpLoader = new DefaultQualityProfileLoader(ws); + } + + @Test + public void testEncoding() throws IOException { + WSLoaderResult<InputStream> result = new WSLoaderResult<>(createEncodedQP("qp"), false); + when(ws.loadStream(anyString())).thenReturn(result); + + List<QualityProfile> loaded = qpLoader.load("foo#2", "my-profile#2", null); + verify(ws).loadStream("/api/qualityprofiles/search.protobuf?projectKey=foo%232&profileName=my-profile%232"); + verifyNoMoreInteractions(ws); + assertThat(loaded).hasSize(1); + } + + @Test + public void testNoProfile() throws IOException { + InputStream is = createEncodedQP(); + when(ws.loadStream(anyString())).thenReturn(new WSLoaderResult<>(is, false)); + + exception.expect(MessageException.class); + exception.expectMessage("No quality profiles"); + + qpLoader.load("project", null, null); + verifyNoMoreInteractions(ws); + } + + @Test + public void use_real_response() throws IOException { + InputStream is = getTestResource("quality_profile_search_default"); + when(ws.loadStream(anyString())).thenReturn(new WSLoaderResult<>(is, false)); + + List<QualityProfile> loaded = qpLoader.loadDefault(null, null); + verify(ws).loadStream("/api/qualityprofiles/search.protobuf?defaults=true"); + verifyNoMoreInteractions(ws); + assertThat(loaded).hasSize(1); + } + + private InputStream getTestResource(String name) throws IOException { + return Resources.asByteSource(this.getClass().getResource(this.getClass().getSimpleName() + "/" + name)) + .openBufferedStream(); + } + + private static InputStream createEncodedQP(String... names) throws IOException { + ByteArrayOutputStream os = new ByteArrayOutputStream(); + QualityProfiles.SearchWsResponse.Builder responseBuilder = QualityProfiles.SearchWsResponse.newBuilder(); + + for (String n : names) { + QualityProfile qp = QualityProfile.newBuilder().setKey(n).setName(n).setLanguage("lang").build(); + responseBuilder.addProfiles(qp); + } + + responseBuilder.build().writeTo(os); + return new ByteArrayInputStream(os.toByteArray()); + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/repository/DefaultServerIssuesLoaderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/repository/DefaultServerIssuesLoaderTest.java new file mode 100644 index 00000000000..22416368f3f --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/repository/DefaultServerIssuesLoaderTest.java @@ -0,0 +1,82 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.repository; + +import org.sonar.batch.cache.WSLoaderResult; +import org.sonar.scanner.protocol.input.ScannerInput; +import org.sonar.scanner.protocol.input.ScannerInput.ServerIssue; +import org.sonar.batch.cache.WSLoader; +import com.google.common.base.Function; +import org.junit.Before; +import org.junit.Test; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class DefaultServerIssuesLoaderTest { + private DefaultServerIssuesLoader loader; + private WSLoader wsLoader; + + @Before + public void prepare() { + wsLoader = mock(WSLoader.class); + loader = new DefaultServerIssuesLoader(wsLoader); + } + + @Test + public void loadFromWs() throws Exception { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + + ServerIssue.newBuilder().setKey("ab1").build() + .writeDelimitedTo(bos); + ServerIssue.newBuilder().setKey("ab2").build() + .writeDelimitedTo(bos); + + InputStream is = new ByteArrayInputStream(bos.toByteArray()); + when(wsLoader.loadStream("/batch/issues.protobuf?key=foo")).thenReturn(new WSLoaderResult<>(is, true)); + + final List<ServerIssue> result = new ArrayList<>(); + loader.load("foo", new Function<ScannerInput.ServerIssue, Void>() { + + @Override + public Void apply(ServerIssue input) { + result.add(input); + return null; + } + }); + + assertThat(result).extracting("key").containsExactly("ab1", "ab2"); + } + + @Test(expected = IllegalStateException.class) + public void testError() throws IOException { + InputStream is = mock(InputStream.class); + when(is.read()).thenThrow(IOException.class); + when(wsLoader.loadStream("/batch/issues.protobuf?key=foo")).thenReturn(new WSLoaderResult<>(is, true)); + loader.load("foo", mock(Function.class)); + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/repository/ProjectRepositoriesProviderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/repository/ProjectRepositoriesProviderTest.java new file mode 100644 index 00000000000..781b48a2a59 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/repository/ProjectRepositoriesProviderTest.java @@ -0,0 +1,115 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.repository; + +import java.util.Date; + +import org.sonar.batch.repository.FileData; +import com.google.common.collect.Table; +import com.google.common.collect.HashBasedTable; +import org.apache.commons.lang.mutable.MutableBoolean; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.sonar.api.batch.bootstrap.ProjectKey; +import org.sonar.batch.analysis.DefaultAnalysisMode; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +public class ProjectRepositoriesProviderTest { + private ProjectRepositoriesProvider provider; + private ProjectRepositories project; + + @Mock + private ProjectRepositoriesLoader loader; + @Mock + private ProjectKey projectKey; + @Mock + private DefaultAnalysisMode mode; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + Table<String, String, String> t1 = HashBasedTable.create(); + Table<String, String, FileData> t2 = HashBasedTable.create(); + + project = new ProjectRepositories(t1, t2, new Date()); + provider = new ProjectRepositoriesProvider(); + + when(projectKey.get()).thenReturn("key"); + } + + @Test + public void testNonAssociated() { + when(mode.isNotAssociated()).thenReturn(true); + ProjectRepositories repo = provider.provide(loader, projectKey, mode); + + assertThat(repo.exists()).isEqualTo(false); + verify(mode).isNotAssociated(); + verifyNoMoreInteractions(loader, projectKey, mode); + } + + @Test + public void singleton() { + when(mode.isNotAssociated()).thenReturn(true); + ProjectRepositories repo = provider.provide(loader, projectKey, mode); + + assertThat(repo.exists()).isEqualTo(false); + verify(mode).isNotAssociated(); + verifyNoMoreInteractions(loader, projectKey, mode); + + repo = provider.provide(loader, projectKey, mode); + verifyNoMoreInteractions(loader, projectKey, mode); + } + + @Test + public void testValidation() { + when(mode.isNotAssociated()).thenReturn(false); + when(mode.isIssues()).thenReturn(true); + when(loader.load(eq("key"), eq(true), any(MutableBoolean.class))).thenReturn(project); + + provider.provide(loader, projectKey, mode); + } + + @Test + public void testAssociated() { + when(mode.isNotAssociated()).thenReturn(false); + when(mode.isIssues()).thenReturn(false); + when(loader.load(eq("key"), eq(false), any(MutableBoolean.class))).thenReturn(project); + + ProjectRepositories repo = provider.provide(loader, projectKey, mode); + + assertThat(repo.exists()).isEqualTo(true); + assertThat(repo.lastAnalysisDate()).isNotNull(); + + verify(mode).isNotAssociated(); + verify(mode, times(2)).isIssues(); + verify(projectKey).get(); + verify(loader).load(eq("key"), eq(false), any(MutableBoolean.class)); + verifyNoMoreInteractions(loader, projectKey, mode); + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/repository/QualityProfileProviderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/repository/QualityProfileProviderTest.java new file mode 100644 index 00000000000..0cd1683b9c9 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/repository/QualityProfileProviderTest.java @@ -0,0 +1,165 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.repository; + +import com.google.common.collect.ImmutableMap; +import java.util.ArrayList; +import java.util.List; +import org.apache.commons.lang.mutable.MutableBoolean; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.sonar.api.batch.bootstrap.ProjectKey; +import org.sonar.api.utils.log.LogTester; +import org.sonar.api.utils.log.LoggerLevel; +import org.sonar.batch.analysis.AnalysisProperties; +import org.sonar.batch.analysis.DefaultAnalysisMode; +import org.sonar.batch.rule.ModuleQProfiles; +import org.sonarqube.ws.QualityProfiles.SearchWsResponse.QualityProfile; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.eq; +import static org.mockito.Matchers.isNull; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +public class QualityProfileProviderTest { + + @Rule + public LogTester logTester = new LogTester(); + + private QualityProfileProvider qualityProfileProvider; + + @Mock + private QualityProfileLoader loader; + @Mock + private DefaultAnalysisMode mode; + @Mock + private AnalysisProperties props; + @Mock + private ProjectKey key; + @Mock + private ProjectRepositories projectRepo; + + private List<QualityProfile> response; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + qualityProfileProvider = new QualityProfileProvider(); + + when(key.get()).thenReturn("project"); + when(projectRepo.exists()).thenReturn(true); + + response = new ArrayList<>(1); + response.add(QualityProfile.newBuilder().setKey("profile").setName("profile").setLanguage("lang").build()); + } + + @Test + public void testProvide() { + when(mode.isNotAssociated()).thenReturn(false); + when(loader.load(eq("project"), isNull(String.class), any(MutableBoolean.class))).thenReturn(response); + ModuleQProfiles qps = qualityProfileProvider.provide(key, loader, projectRepo, props, mode); + assertResponse(qps); + + verify(loader).load(eq("project"), isNull(String.class), any(MutableBoolean.class)); + verifyNoMoreInteractions(loader); + } + + @Test + public void testNonAssociated() { + when(mode.isNotAssociated()).thenReturn(true); + when(loader.loadDefault(anyString(), any(MutableBoolean.class))).thenReturn(response); + ModuleQProfiles qps = qualityProfileProvider.provide(key, loader, projectRepo, props, mode); + assertResponse(qps); + + verify(loader).loadDefault(anyString(), any(MutableBoolean.class)); + verifyNoMoreInteractions(loader); + } + + @Test + public void testProjectDoesntExist() { + when(mode.isNotAssociated()).thenReturn(false); + when(projectRepo.exists()).thenReturn(false); + when(loader.loadDefault(anyString(), any(MutableBoolean.class))).thenReturn(response); + ModuleQProfiles qps = qualityProfileProvider.provide(key, loader, projectRepo, props, mode); + assertResponse(qps); + + verify(loader).loadDefault(anyString(), any(MutableBoolean.class)); + verifyNoMoreInteractions(loader); + } + + @Test + public void testProfileProp() { + when(mode.isNotAssociated()).thenReturn(false); + when(loader.load(eq("project"), eq("custom"), any(MutableBoolean.class))).thenReturn(response); + when(props.property(ModuleQProfiles.SONAR_PROFILE_PROP)).thenReturn("custom"); + when(props.properties()).thenReturn(ImmutableMap.of(ModuleQProfiles.SONAR_PROFILE_PROP, "custom")); + + ModuleQProfiles qps = qualityProfileProvider.provide(key, loader, projectRepo, props, mode); + assertResponse(qps); + + verify(loader).load(eq("project"), eq("custom"), any(MutableBoolean.class)); + verifyNoMoreInteractions(loader); + assertThat(logTester.logs(LoggerLevel.WARN)).contains("Ability to set quality profile from command line using '" + ModuleQProfiles.SONAR_PROFILE_PROP + + "' is deprecated and will be dropped in a future SonarQube version. Please configure quality profile used by your project on SonarQube server."); + } + + @Test + public void testIgnoreSonarProfileIssuesMode() { + when(mode.isNotAssociated()).thenReturn(false); + when(mode.isIssues()).thenReturn(true); + when(loader.load(eq("project"), (String) eq(null), any(MutableBoolean.class))).thenReturn(response); + when(props.property(ModuleQProfiles.SONAR_PROFILE_PROP)).thenReturn("custom"); + + ModuleQProfiles qps = qualityProfileProvider.provide(key, loader, projectRepo, props, mode); + assertResponse(qps); + + verify(loader).load(eq("project"), (String) eq(null), any(MutableBoolean.class)); + verifyNoMoreInteractions(loader); + } + + @Test + public void testProfilePropDefault() { + when(mode.isNotAssociated()).thenReturn(true); + when(loader.loadDefault(eq("custom"), any(MutableBoolean.class))).thenReturn(response); + when(props.property(ModuleQProfiles.SONAR_PROFILE_PROP)).thenReturn("custom"); + when(props.properties()).thenReturn(ImmutableMap.of(ModuleQProfiles.SONAR_PROFILE_PROP, "custom")); + + ModuleQProfiles qps = qualityProfileProvider.provide(key, loader, projectRepo, props, mode); + assertResponse(qps); + + verify(loader).loadDefault(eq("custom"), any(MutableBoolean.class)); + verifyNoMoreInteractions(loader); + assertThat(logTester.logs(LoggerLevel.WARN)).contains("Ability to set quality profile from command line using '" + ModuleQProfiles.SONAR_PROFILE_PROP + + "' is deprecated and will be dropped in a future SonarQube version. Please configure quality profile used by your project on SonarQube server."); + } + + private void assertResponse(ModuleQProfiles qps) { + assertThat(qps.findAll()).hasSize(1); + assertThat(qps.findAll()).extracting("key").containsExactly("profile"); + + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/repository/user/UserRepositoryLoaderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/repository/user/UserRepositoryLoaderTest.java new file mode 100644 index 00000000000..870afd5737e --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/repository/user/UserRepositoryLoaderTest.java @@ -0,0 +1,119 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.repository.user; + +import org.assertj.core.util.Lists; + +import org.sonar.batch.cache.WSLoaderResult; +import org.sonar.scanner.protocol.input.ScannerInput; +import org.sonar.batch.cache.WSLoader; +import org.junit.Before; +import com.google.common.collect.ImmutableList; +import org.apache.commons.lang.mutable.MutableBoolean; +import com.google.common.collect.ImmutableMap; +import org.junit.rules.ExpectedException; +import org.junit.Rule; +import org.mockito.Mockito; +import org.junit.Test; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Arrays; +import java.util.Map; + +import static org.mockito.Matchers.anyString; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.tuple; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class UserRepositoryLoaderTest { + @Rule + public final ExpectedException exception = ExpectedException.none(); + + private WSLoader wsLoader; + private UserRepositoryLoader userRepo; + + @Before + public void setUp() { + wsLoader = mock(WSLoader.class); + userRepo = new UserRepositoryLoader(wsLoader); + } + + @Test + public void testLoadEmptyList() { + assertThat(userRepo.load(Lists.<String>emptyList())).isEmpty(); + } + + @Test + public void testLoad() throws IOException { + Map<String, String> userMap = ImmutableMap.of("fmallet", "Freddy Mallet", "sbrandhof", "Simon"); + WSLoaderResult<InputStream> res = new WSLoaderResult<>(createUsersMock(userMap), true); + when(wsLoader.loadStream("/batch/users?logins=fmallet,sbrandhof")).thenReturn(res); + + assertThat(userRepo.load(Arrays.asList("fmallet", "sbrandhof"))).extracting("login", "name").containsOnly(tuple("fmallet", "Freddy Mallet"), tuple("sbrandhof", "Simon")); + } + + @Test + public void testFromCache() throws IOException { + WSLoaderResult<InputStream> res = new WSLoaderResult<>(createUsersMock(ImmutableMap.of("fmallet", "Freddy Mallet")), true); + when(wsLoader.loadStream(anyString())).thenReturn(res); + MutableBoolean fromCache = new MutableBoolean(); + userRepo.load("", fromCache); + assertThat(fromCache.booleanValue()).isTrue(); + + fromCache.setValue(false); + userRepo.load(ImmutableList.of("user"), fromCache); + assertThat(fromCache.booleanValue()).isTrue(); + } + + @Test + public void testLoadSingleUser() throws IOException { + WSLoaderResult<InputStream> res = new WSLoaderResult<>(createUsersMock(ImmutableMap.of("fmallet", "Freddy Mallet")), true); + when(wsLoader.loadStream("/batch/users?logins=fmallet")).thenReturn(res); + + assertThat(userRepo.load("fmallet").getName()).isEqualTo("Freddy Mallet"); + } + + private InputStream createUsersMock(Map<String, String> users) throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + + for (Map.Entry<String, String> user : users.entrySet()) { + ScannerInput.User.Builder builder = ScannerInput.User.newBuilder(); + builder.setLogin(user.getKey()).setName(user.getValue()).build().writeDelimitedTo(out); + } + return new ByteArrayInputStream(out.toByteArray()); + } + + @Test + public void testInputStreamError() throws IOException { + InputStream is = mock(InputStream.class); + Mockito.doThrow(IOException.class).when(is).read(); + WSLoaderResult<InputStream> res = new WSLoaderResult<>(is, true); + + when(wsLoader.loadStream("/batch/users?logins=fmallet,sbrandhof")).thenReturn(res); + + exception.expect(IllegalStateException.class); + exception.expectMessage("Unable to get user details from server"); + + userRepo.load(Arrays.asList("fmallet", "sbrandhof")); + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/rule/ActiveRulesProviderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/rule/ActiveRulesProviderTest.java new file mode 100644 index 00000000000..1e1b1f0fc3c --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/rule/ActiveRulesProviderTest.java @@ -0,0 +1,97 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.rule; + +import com.google.common.collect.ImmutableList; +import java.util.LinkedList; +import java.util.List; +import org.apache.commons.lang.mutable.MutableBoolean; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.sonar.api.batch.rule.ActiveRules; +import org.sonar.api.rule.RuleKey; +import org.sonarqube.ws.QualityProfiles.SearchWsResponse.QualityProfile; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +public class ActiveRulesProviderTest { + private ActiveRulesProvider provider; + + @Mock + private DefaultActiveRulesLoader loader; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + provider = new ActiveRulesProvider(); + } + + @Test + public void testCombinationOfRules() { + LoadedActiveRule r1 = mockRule("rule1"); + LoadedActiveRule r2 = mockRule("rule2"); + LoadedActiveRule r3 = mockRule("rule3"); + + List<LoadedActiveRule> qp1Rules = ImmutableList.of(r1, r2); + List<LoadedActiveRule> qp2Rules = ImmutableList.of(r2, r3); + List<LoadedActiveRule> qp3Rules = ImmutableList.of(r1, r3); + + when(loader.load(eq("qp1"), any(MutableBoolean.class))).thenReturn(qp1Rules); + when(loader.load(eq("qp2"), any(MutableBoolean.class))).thenReturn(qp2Rules); + when(loader.load(eq("qp3"), any(MutableBoolean.class))).thenReturn(qp3Rules); + + ModuleQProfiles profiles = mockProfiles("qp1", "qp2", "qp3"); + ActiveRules activeRules = provider.provide(loader, profiles); + + assertThat(activeRules.findAll()).hasSize(3); + assertThat(activeRules.findAll()).extracting("ruleKey").containsOnly( + RuleKey.of("rule1", "rule1"), RuleKey.of("rule2", "rule2"), RuleKey.of("rule3", "rule3")); + + verify(loader).load(eq("qp1"), any(MutableBoolean.class)); + verify(loader).load(eq("qp2"), any(MutableBoolean.class)); + verify(loader).load(eq("qp3"), any(MutableBoolean.class)); + verifyNoMoreInteractions(loader); + } + + private static ModuleQProfiles mockProfiles(String... keys) { + List<QualityProfile> profiles = new LinkedList<>(); + + for (String k : keys) { + QualityProfile p = QualityProfile.newBuilder().setKey(k).setLanguage(k).build(); + profiles.add(p); + } + + return new ModuleQProfiles(profiles); + } + + private static LoadedActiveRule mockRule(String name) { + LoadedActiveRule r = new LoadedActiveRule(); + r.setName(name); + r.setRuleKey(RuleKey.of(name, name)); + return r; + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/rule/DefaultActiveRulesLoaderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/rule/DefaultActiveRulesLoaderTest.java new file mode 100644 index 00000000000..434c84dda93 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/rule/DefaultActiveRulesLoaderTest.java @@ -0,0 +1,85 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.rule; + +import org.sonar.api.rule.RuleKey; +import org.sonar.batch.cache.WSLoaderResult; +import org.sonar.batch.cache.WSLoader; +import com.google.common.io.Resources; +import org.junit.Test; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Collection; + +import static org.mockito.Mockito.verify; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; +import org.junit.Before; + +public class DefaultActiveRulesLoaderTest { + private DefaultActiveRulesLoader loader; + private WSLoader ws; + + @Before + public void setUp() { + ws = mock(WSLoader.class); + loader = new DefaultActiveRulesLoader(ws); + } + + @Test + public void feed_real_response_encode_qp() throws IOException { + InputStream response1 = loadResource("active_rule_search1.protobuf"); + InputStream response2 = loadResource("active_rule_search2.protobuf"); + + String req1 = "/api/rules/search.protobuf?f=repo,name,severity,lang,internalKey,templateKey,params,actives&activation=true&qprofile=c%2B-test_c%2B-values-17445&p=1&ps=500"; + String req2 = "/api/rules/search.protobuf?f=repo,name,severity,lang,internalKey,templateKey,params,actives&activation=true&qprofile=c%2B-test_c%2B-values-17445&p=2&ps=500"; + when(ws.loadStream(req1)).thenReturn(new WSLoaderResult<>(response1, false)); + when(ws.loadStream(req2)).thenReturn(new WSLoaderResult<>(response2, false)); + + Collection<LoadedActiveRule> activeRules = loader.load("c+-test_c+-values-17445", null); + assertThat(activeRules).hasSize(226); + assertActiveRule(activeRules); + + verify(ws).loadStream(req1); + verify(ws).loadStream(req2); + verifyNoMoreInteractions(ws); + } + + private static void assertActiveRule(Collection<LoadedActiveRule> activeRules) { + RuleKey key = RuleKey.of("squid", "S3008"); + for (LoadedActiveRule r : activeRules) { + if (!r.getRuleKey().equals(key)) { + continue; + } + + assertThat(r.getParams().get("format")).isEqualTo("^[a-z][a-zA-Z0-9]*$"); + assertThat(r.getSeverity()).isEqualTo("MINOR"); + } + } + + private InputStream loadResource(String name) throws IOException { + return Resources.asByteSource(this.getClass().getResource("DefaultActiveRulesLoaderTest/" + name)) + .openBufferedStream(); + } + +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/rule/DefaultRulesLoaderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/rule/DefaultRulesLoaderTest.java new file mode 100644 index 00000000000..a5a461c4e9f --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/rule/DefaultRulesLoaderTest.java @@ -0,0 +1,79 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.rule; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import org.junit.rules.ExpectedException; +import org.sonar.batch.cache.WSLoaderResult; +import org.sonar.batch.cache.WSLoader; +import org.apache.commons.lang.mutable.MutableBoolean; +import org.sonarqube.ws.Rules.ListResponse.Rule; +import com.google.common.io.ByteSource; +import com.google.common.io.Resources; + +import java.io.IOException; +import java.io.InputStream; +import java.util.List; + +import static org.mockito.Matchers.anyString; +import static org.assertj.core.api.Assertions.assertThat; +import org.junit.Test; + +public class DefaultRulesLoaderTest { + @org.junit.Rule + public ExpectedException exception = ExpectedException.none(); + + @Test + public void testParseServerResponse() throws IOException { + WSLoader wsLoader = mock(WSLoader.class); + InputStream is = Resources.asByteSource(this.getClass().getResource("DefaultRulesLoader/response.protobuf")).openBufferedStream(); + when(wsLoader.loadStream(anyString())).thenReturn(new WSLoaderResult<>(is, true)); + DefaultRulesLoader loader = new DefaultRulesLoader(wsLoader); + List<Rule> ruleList = loader.load(null); + assertThat(ruleList).hasSize(318); + } + + @Test + public void testLoadedFromCache() throws IOException { + WSLoader wsLoader = mock(WSLoader.class); + InputStream is = Resources.asByteSource(this.getClass().getResource("DefaultRulesLoader/response.protobuf")).openBufferedStream(); + when(wsLoader.loadStream(anyString())).thenReturn(new WSLoaderResult<>(is, true)); + DefaultRulesLoader loader = new DefaultRulesLoader(wsLoader); + MutableBoolean fromCache = new MutableBoolean(); + loader.load(fromCache); + + assertThat(fromCache.booleanValue()).isTrue(); + } + + @Test + public void testError() throws IOException { + WSLoader wsLoader = mock(WSLoader.class); + InputStream is = ByteSource.wrap(new String("trash").getBytes()).openBufferedStream(); + when(wsLoader.loadStream(anyString())).thenReturn(new WSLoaderResult<>(is, true)); + DefaultRulesLoader loader = new DefaultRulesLoader(wsLoader); + + exception.expect(IllegalStateException.class); + exception.expectMessage("Unable to get rules"); + + loader.load(null); + } + +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/rule/QProfileSensorTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/rule/QProfileSensorTest.java new file mode 100644 index 00000000000..4801f246c55 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/rule/QProfileSensorTest.java @@ -0,0 +1,135 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.rule; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.sonar.api.batch.AnalysisMode; +import org.sonar.api.batch.SensorContext; +import org.sonar.api.batch.fs.internal.DefaultFileSystem; +import org.sonar.api.measures.CoreMetrics; +import org.sonar.api.resources.Project; +import org.sonar.api.test.IsMeasure; +import org.sonar.core.util.UtcDateUtils; + +import java.util.Collections; +import java.util.Date; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Matchers.argThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class QProfileSensorTest { + + @Rule + public TemporaryFolder temp = new TemporaryFolder(); + + static final Date DATE = UtcDateUtils.parseDateTime("2014-01-15T12:00:00+0000"); + static final QProfile JAVA_PROFILE = new QProfile().setKey("java-two").setName("Java Two").setLanguage("java") + .setRulesUpdatedAt(DATE); + static final QProfile PHP_PROFILE = new QProfile().setKey("php-one").setName("Php One").setLanguage("php") + .setRulesUpdatedAt(DATE); + + ModuleQProfiles moduleQProfiles = mock(ModuleQProfiles.class); + Project project = mock(Project.class); + SensorContext sensorContext = mock(SensorContext.class); + DefaultFileSystem fs; + + @Before + public void prepare() throws Exception { + fs = new DefaultFileSystem(temp.newFolder().toPath()); + } + + @Test + public void to_string() { + QProfileSensor sensor = new QProfileSensor(moduleQProfiles, fs, mock(AnalysisMode.class)); + assertThat(sensor.toString()).isEqualTo("QProfileSensor"); + } + + @Test + public void no_execution_in_issues_mode() { + AnalysisMode analysisMode = mock(AnalysisMode.class); + when(analysisMode.isIssues()).thenReturn(true); + QProfileSensor sensor = new QProfileSensor(moduleQProfiles, fs, analysisMode); + assertThat(sensor.shouldExecuteOnProject(project)).isFalse(); + + } + + @Test + public void no_qprofiles() { + when(moduleQProfiles.findAll()).thenReturn(Collections.<QProfile>emptyList()); + + QProfileSensor sensor = new QProfileSensor(moduleQProfiles, fs, mock(AnalysisMode.class)); + assertThat(sensor.shouldExecuteOnProject(project)).isTrue(); + sensor.analyse(project, sensorContext); + + // measures are not saved + verify(sensorContext).saveMeasure(argThat(new IsMeasure(CoreMetrics.QUALITY_PROFILES, "[]"))); + } + + @Test + public void mark_profiles_as_used() { + when(moduleQProfiles.findByLanguage("java")).thenReturn(JAVA_PROFILE); + when(moduleQProfiles.findByLanguage("php")).thenReturn(PHP_PROFILE); + when(moduleQProfiles.findByLanguage("abap")).thenReturn(null); + fs.addLanguages("java", "php", "abap"); + + QProfileSensor sensor = new QProfileSensor(moduleQProfiles, fs, mock(AnalysisMode.class)); + assertThat(sensor.shouldExecuteOnProject(project)).isTrue(); + sensor.analyse(project, sensorContext); + } + + @Test + public void store_measures_on_single_lang_module() { + when(moduleQProfiles.findByLanguage("java")).thenReturn(JAVA_PROFILE); + when(moduleQProfiles.findByLanguage("php")).thenReturn(PHP_PROFILE); + when(moduleQProfiles.findByLanguage("abap")).thenReturn(null); + fs.addLanguages("java"); + + QProfileSensor sensor = new QProfileSensor(moduleQProfiles, fs, mock(AnalysisMode.class)); + assertThat(sensor.shouldExecuteOnProject(project)).isTrue(); + sensor.analyse(project, sensorContext); + + verify(sensorContext).saveMeasure( + argThat(new IsMeasure(CoreMetrics.QUALITY_PROFILES, + "[{\"key\":\"java-two\",\"language\":\"java\",\"name\":\"Java Two\",\"rulesUpdatedAt\":\"2014-01-15T12:00:00+0000\"}]"))); + } + + @Test + public void store_measures_on_multi_lang_module() { + when(moduleQProfiles.findByLanguage("java")).thenReturn(JAVA_PROFILE); + when(moduleQProfiles.findByLanguage("php")).thenReturn(PHP_PROFILE); + when(moduleQProfiles.findByLanguage("abap")).thenReturn(null); + fs.addLanguages("java", "php"); + + QProfileSensor sensor = new QProfileSensor(moduleQProfiles, fs, mock(AnalysisMode.class)); + assertThat(sensor.shouldExecuteOnProject(project)).isTrue(); + sensor.analyse(project, sensorContext); + + verify(sensorContext).saveMeasure( + argThat(new IsMeasure(CoreMetrics.QUALITY_PROFILES, + "[{\"key\":\"java-two\",\"language\":\"java\",\"name\":\"Java Two\",\"rulesUpdatedAt\":\"2014-01-15T12:00:00+0000\"}," + + "{\"key\":\"php-one\",\"language\":\"php\",\"name\":\"Php One\",\"rulesUpdatedAt\":\"2014-01-15T12:00:00+0000\"}]"))); + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/rule/QProfileVerifierTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/rule/QProfileVerifierTest.java new file mode 100644 index 00000000000..9df8cd75491 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/rule/QProfileVerifierTest.java @@ -0,0 +1,101 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.rule; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.rules.TemporaryFolder; +import org.slf4j.Logger; +import org.sonar.api.batch.fs.internal.DefaultFileSystem; +import org.sonar.api.config.Settings; +import org.sonar.api.utils.MessageException; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class QProfileVerifierTest { + + @Rule + public TemporaryFolder temp = new TemporaryFolder(); + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + private DefaultFileSystem fs; + private ModuleQProfiles profiles; + private Settings settings = new Settings(); + + @Before + public void before() throws Exception { + fs = new DefaultFileSystem(temp.newFolder().toPath()); + profiles = mock(ModuleQProfiles.class); + QProfile javaProfile = new QProfile().setKey("p1").setName("My Java profile").setLanguage("java"); + when(profiles.findByLanguage("java")).thenReturn(javaProfile); + QProfile cobolProfile = new QProfile().setKey("p2").setName("My Cobol profile").setLanguage("cobol"); + when(profiles.findByLanguage("cobol")).thenReturn(cobolProfile); + } + + @Test + public void should_log_all_used_profiles() { + fs.addLanguages("java", "cobol"); + QProfileVerifier profileLogger = new QProfileVerifier(settings, fs, profiles); + Logger logger = mock(Logger.class); + profileLogger.execute(logger); + + verify(logger).info("Quality profile for {}: {}", "java", "My Java profile"); + verify(logger).info("Quality profile for {}: {}", "cobol", "My Cobol profile"); + } + + @Test + public void should_fail_if_default_profile_not_used() { + fs.addLanguages("java", "cobol"); + settings.setProperty("sonar.profile", "Unknown"); + + QProfileVerifier profileLogger = new QProfileVerifier(settings, fs, profiles); + + thrown.expect(MessageException.class); + thrown.expectMessage("sonar.profile was set to 'Unknown' but didn't match any profile for any language. Please check your configuration."); + + profileLogger.execute(); + } + + @Test + public void should_not_fail_if_no_language_on_project() { + settings.setProperty("sonar.profile", "Unknown"); + + QProfileVerifier profileLogger = new QProfileVerifier(settings, fs, profiles); + + profileLogger.execute(); + + } + + @Test + public void should_not_fail_if_default_profile_used_at_least_once() { + fs.addLanguages("java", "cobol"); + settings.setProperty("sonar.profile", "My Java profile"); + + QProfileVerifier profileLogger = new QProfileVerifier(settings, fs, profiles); + + profileLogger.execute(); + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/rule/RuleFinderCompatibilityTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/rule/RuleFinderCompatibilityTest.java new file mode 100644 index 00000000000..abc5f5dfd75 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/rule/RuleFinderCompatibilityTest.java @@ -0,0 +1,101 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.rule; + +import org.sonar.api.batch.rule.internal.RulesBuilder; + +import org.sonar.api.batch.rule.Rules; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.sonar.api.rule.RuleKey; +import org.sonar.api.rules.RuleQuery; +import static org.assertj.core.api.Assertions.assertThat; + +public class RuleFinderCompatibilityTest { + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + private Rules rules; + private RuleFinderCompatibility ruleFinder; + + @Before + public void prepare() { + RulesBuilder builder = new RulesBuilder(); + builder.add(RuleKey.of("repo1", "rule1")); + builder.add(RuleKey.of("repo1", "rule2")).setInternalKey("rule2_internal"); + builder.add(RuleKey.of("repo2", "rule1")); + rules = builder.build(); + + ruleFinder = new RuleFinderCompatibility(rules); + } + + @Test + public void testByInternalKey() { + assertThat(ruleFinder.find(RuleQuery.create().withRepositoryKey("repo1").withConfigKey("rule2_internal")).getKey()).isEqualTo("rule2"); + assertThat(ruleFinder.find(RuleQuery.create().withRepositoryKey("repo1").withConfigKey("rule2_internal2"))).isNull(); + } + + @Test + public void testByKey() { + assertThat(ruleFinder.find(RuleQuery.create().withRepositoryKey("repo1").withKey("rule2")).getKey()).isEqualTo("rule2"); + assertThat(ruleFinder.find(RuleQuery.create().withRepositoryKey("repo1").withKey("rule3"))).isNull(); + assertThat(ruleFinder.findByKey("repo1", "rule2").getKey()).isEqualTo("rule2"); + } + + @Test + public void duplicateResult() { + thrown.expect(IllegalArgumentException.class); + thrown.expectMessage("Non unique result for rule query: RuleQuery[repositoryKey=repo1,key=<null>,configKey=<null>]"); + ruleFinder.find(RuleQuery.create().withRepositoryKey("repo1")); + } + + @Test + public void unsupportedById() { + thrown.expect(UnsupportedOperationException.class); + ruleFinder.findById(1); + } + + @Test + public void unsupportedByInternalKeyWithoutRepo() { + thrown.expect(UnsupportedOperationException.class); + ruleFinder.find(RuleQuery.create().withConfigKey("config")); + } + + @Test + public void unsupportedByKeyWithoutRepo() { + thrown.expect(UnsupportedOperationException.class); + ruleFinder.find(RuleQuery.create().withKey("key")); + } + + @Test + public void unsupportedByKeyAndInternalKey() { + thrown.expect(UnsupportedOperationException.class); + ruleFinder.find(RuleQuery.create().withRepositoryKey("repo").withKey("key").withConfigKey("config")); + } + + @Test + public void unsupportedByKeyAndInternalKeyWithoutRepo() { + thrown.expect(UnsupportedOperationException.class); + ruleFinder.find(RuleQuery.create().withKey("key").withConfigKey("config")); + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/rule/RulesProfileProviderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/rule/RulesProfileProviderTest.java new file mode 100644 index 00000000000..208a187a966 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/rule/RulesProfileProviderTest.java @@ -0,0 +1,86 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.rule; + +import java.util.Arrays; +import org.junit.Test; +import org.sonar.api.batch.rule.internal.ActiveRulesBuilder; +import org.sonar.api.config.Settings; +import org.sonar.api.profiles.RulesProfile; +import org.sonar.api.rule.RuleKey; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.fail; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class RulesProfileProviderTest { + + ModuleQProfiles qProfiles = mock(ModuleQProfiles.class); + Settings settings = new Settings(); + RulesProfileProvider provider = new RulesProfileProvider(); + + @Test + public void merge_profiles() { + QProfile qProfile = new QProfile().setKey("java-sw").setName("Sonar way").setLanguage("java"); + when(qProfiles.findAll()).thenReturn(Arrays.asList(qProfile)); + + RulesProfile profile = provider.provide(qProfiles, new ActiveRulesBuilder().build(), settings); + + // merge of all profiles + assertThat(profile).isNotNull().isInstanceOf(RulesProfileWrapper.class); + assertThat(profile.getLanguage()).isEqualTo(""); + assertThat(profile.getName()).isEqualTo("SonarQube"); + assertThat(profile.getActiveRules()).isEmpty(); + try { + profile.getId(); + fail(); + } catch (IllegalStateException e) { + // id must not be used at all + } + } + + @Test + public void keep_compatibility_with_single_language_projects() { + settings.setProperty("sonar.language", "java"); + + QProfile qProfile = new QProfile().setKey("java-sw").setName("Sonar way").setLanguage("java"); + when(qProfiles.findByLanguage("java")).thenReturn(qProfile); + + RulesProfile profile = provider.provide(qProfiles, new ActiveRulesBuilder().build(), settings); + + // no merge, directly the old hibernate profile + assertThat(profile).isNotNull(); + assertThat(profile.getLanguage()).isEqualTo("java"); + assertThat(profile.getName()).isEqualTo("Sonar way"); + } + + @Test + public void support_rule_templates() { + QProfile qProfile = new QProfile().setKey("java-sw").setName("Sonar way").setLanguage("java"); + when(qProfiles.findAll()).thenReturn(Arrays.asList(qProfile)); + ActiveRulesBuilder activeRulesBuilder = new ActiveRulesBuilder(); + activeRulesBuilder.create(RuleKey.of("java", "S001")).setTemplateRuleKey("T001").setLanguage("java").activate(); + + RulesProfile profile = provider.provide(qProfiles, activeRulesBuilder.build(), settings); + + assertThat(profile.getActiveRule("java", "S001").getRule().getTemplate().getKey()).isEqualTo("T001"); + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/rule/RulesProviderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/rule/RulesProviderTest.java new file mode 100644 index 00000000000..2df29eaa61a --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/rule/RulesProviderTest.java @@ -0,0 +1,66 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.rule; + +import static org.mockito.Matchers.any; + +import org.apache.commons.lang.mutable.MutableBoolean; + +import com.google.common.collect.Lists; +import org.sonar.api.batch.rule.Rules; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.when; +import static org.mockito.Mockito.mock; +import org.sonarqube.ws.Rules.ListResponse.Rule; +import org.junit.Test; + +public class RulesProviderTest { + @Test + public void testRuleTranslation() { + RulesLoader loader = mock(RulesLoader.class); + when(loader.load(any(MutableBoolean.class))).thenReturn(Lists.newArrayList(getTestRule())); + + RulesProvider provider = new RulesProvider(); + + Rules rules = provider.provide(loader); + + assertThat(rules.findAll()).hasSize(1); + assertRule(rules.findAll().iterator().next()); + } + + private static void assertRule(org.sonar.api.batch.rule.Rule r) { + Rule testRule = getTestRule(); + + assertThat(r.name()).isEqualTo(testRule.getName()); + assertThat(r.internalKey()).isEqualTo(testRule.getInternalKey()); + assertThat(r.key().rule()).isEqualTo(testRule.getKey()); + assertThat(r.key().repository()).isEqualTo(testRule.getRepository()); + } + + private static Rule getTestRule() { + Rule.Builder ruleBuilder = Rule.newBuilder(); + ruleBuilder.setKey("key1"); + ruleBuilder.setRepository("repo1"); + ruleBuilder.setName("name"); + ruleBuilder.setInternalKey("key1"); + return ruleBuilder.build(); + + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/rule/UsedQProfilesTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/rule/UsedQProfilesTest.java new file mode 100644 index 00000000000..dd46501e68e --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/rule/UsedQProfilesTest.java @@ -0,0 +1,72 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.rule; + +import org.junit.Test; +import org.sonar.core.util.UtcDateUtils; + +import java.util.Arrays; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; + +public class UsedQProfilesTest { + + static final String JAVA_JSON = "{\"key\":\"p1\",\"language\":\"java\",\"name\":\"Sonar Way\",\"rulesUpdatedAt\":\"2014-01-15T00:00:00+0000\"}"; + static final String PHP_JSON = "{\"key\":\"p2\",\"language\":\"php\",\"name\":\"Sonar Way\",\"rulesUpdatedAt\":\"2014-02-20T00:00:00+0000\"}"; + + @Test + public void from_and_to_json() { + QProfile java = new QProfile().setKey("p1").setName("Sonar Way").setLanguage("java") + .setRulesUpdatedAt(UtcDateUtils.parseDateTime("2014-01-15T00:00:00+0000")); + QProfile php = new QProfile().setKey("p2").setName("Sonar Way").setLanguage("php") + .setRulesUpdatedAt(UtcDateUtils.parseDateTime("2014-02-20T00:00:00+0000")); + + UsedQProfiles used = new UsedQProfiles().add(java).add(php); + String json = "[" + JAVA_JSON + "," + PHP_JSON + "]"; + assertThat(used.toJson()).isEqualTo(json); + + used = UsedQProfiles.fromJson(json); + assertThat(used.profiles()).hasSize(2); + assertThat(used.profiles().first().getKey()).isEqualTo("p1"); + assertThat(used.profiles().last().getKey()).isEqualTo("p2"); + } + + @Test + public void do_not_duplicate_profiles() { + QProfile java = new QProfile().setKey("p1").setName("Sonar Way").setLanguage("java"); + QProfile php = new QProfile().setKey("p2").setName("Sonar Way").setLanguage("php"); + + UsedQProfiles used = new UsedQProfiles().addAll(Arrays.asList(java, java, php)); + assertThat(used.profiles()).hasSize(2); + } + + @Test + public void group_profiles_by_key() { + QProfile java = new QProfile().setKey("p1").setName("Sonar Way").setLanguage("java"); + QProfile php = new QProfile().setKey("p2").setName("Sonar Way").setLanguage("php"); + + UsedQProfiles used = new UsedQProfiles().addAll(Arrays.asList(java, java, php)); + Map<String, QProfile> map = used.profilesByKey(); + assertThat(map).hasSize(2); + assertThat(map.get("p1")).isSameAs(java); + assertThat(map.get("p2")).isSameAs(php); + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/LanguageVerifierTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/LanguageVerifierTest.java new file mode 100644 index 00000000000..22a4f21091f --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/LanguageVerifierTest.java @@ -0,0 +1,99 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.scan; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.rules.TemporaryFolder; +import org.sonar.api.batch.fs.internal.DefaultFileSystem; +import org.sonar.api.config.Settings; +import org.sonar.api.resources.Java; +import org.sonar.api.resources.Languages; +import org.sonar.api.utils.MessageException; +import org.sonar.batch.repository.language.DefaultLanguagesRepository; +import org.sonar.batch.repository.language.LanguagesRepository; + +import static org.assertj.core.api.Assertions.assertThat; + +public class LanguageVerifierTest { + + @Rule + public TemporaryFolder temp = new TemporaryFolder(); + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + private Settings settings = new Settings(); + private LanguagesRepository languages = new DefaultLanguagesRepository(new Languages(Java.INSTANCE)); + private DefaultFileSystem fs; + + @Before + public void prepare() throws Exception { + fs = new DefaultFileSystem(temp.newFolder().toPath()); + } + + @Test + public void language_is_not_set() { + LanguageVerifier verifier = new LanguageVerifier(settings, languages, fs); + verifier.start(); + + // no failure and no language is forced + assertThat(fs.languages()).isEmpty(); + + verifier.stop(); + } + + @Test + public void language_is_empty() { + settings.setProperty("sonar.language", ""); + LanguageVerifier verifier = new LanguageVerifier(settings, languages, fs); + verifier.start(); + + // no failure and no language is forced + assertThat(fs.languages()).isEmpty(); + + verifier.stop(); + } + + @Test + public void language_is_valid() { + settings.setProperty("sonar.language", "java"); + + LanguageVerifier verifier = new LanguageVerifier(settings, languages, fs); + verifier.start(); + + // no failure and language is hardly registered + assertThat(fs.languages()).contains("java"); + + verifier.stop(); + } + + @Test + public void language_is_not_valid() { + thrown.expect(MessageException.class); + thrown.expectMessage("You must install a plugin that supports the language 'php'"); + + settings.setProperty("sonar.language", "php"); + LanguageVerifier verifier = new LanguageVerifier(settings, languages, fs); + verifier.start(); + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/ModuleSettingsTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/ModuleSettingsTest.java new file mode 100644 index 00000000000..88fb18f06a4 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/ModuleSettingsTest.java @@ -0,0 +1,162 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.scan; + +import com.google.common.collect.HashBasedTable; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableTable; +import com.google.common.collect.Table; +import java.util.List; +import java.util.Map; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.sonar.api.batch.bootstrap.ProjectDefinition; +import org.sonar.api.config.PropertyDefinitions; +import org.sonar.api.utils.MessageException; +import org.sonar.batch.analysis.DefaultAnalysisMode; +import org.sonar.batch.bootstrap.GlobalSettings; +import org.sonar.batch.report.AnalysisContextReportPublisher; +import org.sonar.batch.repository.FileData; +import org.sonar.batch.repository.ProjectRepositories; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class ModuleSettingsTest { + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + private DefaultAnalysisMode mode; + + @Before + public void before() { + mode = mock(DefaultAnalysisMode.class); + } + + private ProjectRepositories createSettings(String module, Map<String, String> settingsMap) { + Table<String, String, FileData> fileData = ImmutableTable.of(); + Table<String, String, String> settings = HashBasedTable.create(); + + for (Map.Entry<String, String> e : settingsMap.entrySet()) { + settings.put(module, e.getKey(), e.getValue()); + } + return new ProjectRepositories(settings, fileData, null); + } + + @Test + public void testOrderedProjects() { + ProjectDefinition grandParent = ProjectDefinition.create(); + ProjectDefinition parent = ProjectDefinition.create(); + ProjectDefinition child = ProjectDefinition.create(); + grandParent.addSubProject(parent); + parent.addSubProject(child); + + List<ProjectDefinition> hierarchy = ModuleSettings.getTopDownParentProjects(child); + assertThat(hierarchy.get(0)).isEqualTo(grandParent); + assertThat(hierarchy.get(1)).isEqualTo(parent); + assertThat(hierarchy.get(2)).isEqualTo(child); + } + + @Test + public void test_loading_of_module_settings() { + GlobalSettings globalSettings = mock(GlobalSettings.class); + when(globalSettings.getDefinitions()).thenReturn(new PropertyDefinitions()); + when(globalSettings.getProperties()).thenReturn(ImmutableMap.of( + "overridding", "batch", + "on-batch", "true")); + + ProjectRepositories projRepos = createSettings("struts-core", ImmutableMap.of("on-module", "true", "overridding", "module")); + + ProjectDefinition module = ProjectDefinition.create().setKey("struts-core"); + + ModuleSettings moduleSettings = new ModuleSettings(globalSettings, module, projRepos, mode, mock(AnalysisContextReportPublisher.class)); + + assertThat(moduleSettings.getString("overridding")).isEqualTo("module"); + assertThat(moduleSettings.getString("on-batch")).isEqualTo("true"); + assertThat(moduleSettings.getString("on-module")).isEqualTo("true"); + + } + + // SONAR-6386 + @Test + public void test_loading_of_parent_module_settings_for_new_module() { + GlobalSettings globalSettings = mock(GlobalSettings.class); + when(globalSettings.getDefinitions()).thenReturn(new PropertyDefinitions()); + when(globalSettings.getProperties()).thenReturn(ImmutableMap.of( + "overridding", "batch", + "on-batch", "true")); + + ProjectRepositories projRepos = createSettings("struts", ImmutableMap.of("on-module", "true", "overridding", "module")); + + ProjectDefinition module = ProjectDefinition.create().setKey("struts-core"); + ProjectDefinition.create().setKey("struts").addSubProject(module); + + ModuleSettings moduleSettings = new ModuleSettings(globalSettings, module, projRepos, mode, mock(AnalysisContextReportPublisher.class)); + + assertThat(moduleSettings.getString("overridding")).isEqualTo("module"); + assertThat(moduleSettings.getString("on-batch")).isEqualTo("true"); + assertThat(moduleSettings.getString("on-module")).isEqualTo("true"); + } + + @Test + public void should_not_fail_when_accessing_secured_properties() { + GlobalSettings batchSettings = mock(GlobalSettings.class); + when(batchSettings.getDefinitions()).thenReturn(new PropertyDefinitions()); + when(batchSettings.getProperties()).thenReturn(ImmutableMap.of( + "sonar.foo.secured", "bar")); + + ProjectRepositories projSettingsRepo = createSettings("struts-core", ImmutableMap.of("sonar.foo.license.secured", "bar2")); + + ProjectDefinition module = ProjectDefinition.create().setKey("struts-core"); + + ModuleSettings moduleSettings = new ModuleSettings(batchSettings, module, projSettingsRepo, mode, mock(AnalysisContextReportPublisher.class)); + + assertThat(moduleSettings.getString("sonar.foo.license.secured")).isEqualTo("bar2"); + assertThat(moduleSettings.getString("sonar.foo.secured")).isEqualTo("bar"); + } + + @Test + public void should_fail_when_accessing_secured_properties_in_issues() { + GlobalSettings batchSettings = mock(GlobalSettings.class); + when(batchSettings.getDefinitions()).thenReturn(new PropertyDefinitions()); + when(batchSettings.getProperties()).thenReturn(ImmutableMap.of( + "sonar.foo.secured", "bar")); + + ProjectRepositories projSettingsRepo = createSettings("struts-core", ImmutableMap.of("sonar.foo.license.secured", "bar2")); + + when(mode.isIssues()).thenReturn(true); + + ProjectDefinition module = ProjectDefinition.create().setKey("struts-core"); + + ModuleSettings moduleSettings = new ModuleSettings(batchSettings, module, projSettingsRepo, mode, mock(AnalysisContextReportPublisher.class)); + + assertThat(moduleSettings.getString("sonar.foo.license.secured")).isEqualTo("bar2"); + + thrown.expect(MessageException.class); + thrown + .expectMessage( + "Access to the secured property 'sonar.foo.secured' is not possible in issues mode. The SonarQube plugin which requires this property must be deactivated in issues mode."); + moduleSettings.getString("sonar.foo.secured"); + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/ProjectExclusionsTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/ProjectExclusionsTest.java new file mode 100644 index 00000000000..4f9b403a94e --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/ProjectExclusionsTest.java @@ -0,0 +1,122 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.scan; + +import org.junit.Test; +import org.sonar.api.batch.bootstrap.ProjectDefinition; +import org.sonar.api.batch.bootstrap.ProjectReactor; +import org.sonar.api.config.Settings; + +import static org.assertj.core.api.Assertions.assertThat; + +public class ProjectExclusionsTest { + + ProjectReactor newReactor(String rootKey, String... moduleKeys) { + ProjectDefinition root = ProjectDefinition.create().setKey(rootKey); + for (String moduleKey : moduleKeys) { + ProjectDefinition module = ProjectDefinition.create().setKey(moduleKey); + root.addSubProject(module); + } + return new ProjectReactor(root); + } + + @Test + public void testSkippedModules() { + Settings settings = new Settings(); + settings.setProperty("sonar.skippedModules", "sub1,sub3"); + + ProjectReactor reactor = newReactor("root", "sub1", "sub2"); + + ProjectExclusions exclusions = new ProjectExclusions(settings); + exclusions.apply(reactor); + + assertThat(reactor.getProject("root")).isNotNull(); + assertThat(reactor.getProject("sub1")).isNull(); + assertThat(reactor.getProject("sub2")).isNotNull(); + } + + @Test + public void testNoSkippedModules() { + Settings settings = new Settings(); + ProjectReactor reactor = newReactor("root", "sub1", "sub2"); + ProjectExclusions exclusions = new ProjectExclusions(settings); + exclusions.apply(reactor); + + assertThat(reactor.getProject("root")).isNotNull(); + assertThat(reactor.getProject("sub1")).isNotNull(); + assertThat(reactor.getProject("sub2")).isNotNull(); + } + + @Test + public void testIncludedModules() { + Settings settings = new Settings(); + settings.setProperty("sonar.includedModules", "sub1"); + ProjectReactor reactor = newReactor("root", "sub1", "sub2"); + ProjectExclusions exclusions = new ProjectExclusions(settings); + exclusions.apply(reactor); + + assertThat(reactor.getProject("root")).isNotNull(); + assertThat(reactor.getProject("sub1")).isNotNull(); + assertThat(reactor.getProject("sub2")).isNull(); + } + + @Test + public void shouldBeExcludedIfParentIsExcluded() { + ProjectDefinition sub11 = ProjectDefinition.create().setKey("sub11"); + ProjectDefinition sub1 = ProjectDefinition.create().setKey("sub1").addSubProject(sub11); + ProjectDefinition root = ProjectDefinition.create().setKey("root").addSubProject(sub1); + + Settings settings = new Settings(); + settings.setProperty("sonar.skippedModules", "sub1"); + + ProjectReactor reactor = new ProjectReactor(root); + ProjectExclusions exclusions = new ProjectExclusions(settings); + exclusions.apply(reactor); + + assertThat(reactor.getProject("root")).isNotNull(); + assertThat(reactor.getProject("sub1")).isNull(); + assertThat(reactor.getProject("sub11")).isNull(); + } + + @Test(expected = IllegalArgumentException.class) + public void shouldFailIfExcludingRoot() { + Settings settings = new Settings(); + settings.setProperty("sonar.skippedModules", "sub1,root"); + + ProjectReactor reactor = newReactor("root", "sub1", "sub2"); + ProjectExclusions exclusions = new ProjectExclusions(settings); + exclusions.apply(reactor); + } + + @Test + public void shouldIgnoreMavenGroupId() { + ProjectReactor reactor = newReactor("org.apache.struts:struts", "org.apache.struts:struts-core", "org.apache.struts:struts-taglib"); + + Settings settings = new Settings(); + settings.setProperty("sonar.skippedModules", "struts-taglib"); + + ProjectExclusions exclusions = new ProjectExclusions(settings); + exclusions.apply(reactor); + + assertThat(reactor.getProject("org.apache.struts:struts")).isNotNull(); + assertThat(reactor.getProject("org.apache.struts:struts-core")).isNotNull(); + assertThat(reactor.getProject("org.apache.struts:struts-taglib")).isNull(); + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/ProjectLockTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/ProjectLockTest.java new file mode 100644 index 00000000000..c03240b7341 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/ProjectLockTest.java @@ -0,0 +1,103 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.scan; + +import org.junit.rules.ExpectedException; +import org.sonar.api.batch.bootstrap.ProjectDefinition; +import org.sonar.api.batch.bootstrap.ProjectReactor; +import org.sonar.home.cache.DirectoryLock; + +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.assertj.core.api.Assertions.assertThat; +import org.junit.Test; +import org.junit.Before; +import org.junit.Rule; +import org.junit.rules.TemporaryFolder; + +public class ProjectLockTest { + @Rule + public TemporaryFolder tempFolder = new TemporaryFolder(); + + @Rule + public ExpectedException exception = ExpectedException.none(); + private ProjectLock lock; + + @Before + public void setUp() { + lock = setUpTest(tempFolder.getRoot()); + } + + private ProjectLock setUpTest(File file) { + ProjectReactor projectReactor = mock(ProjectReactor.class); + ProjectDefinition projectDefinition = mock(ProjectDefinition.class); + when(projectReactor.getRoot()).thenReturn(projectDefinition); + when(projectDefinition.getWorkDir()).thenReturn(file); + + return new ProjectLock(projectReactor); + } + + @Test + public void tryLock() { + Path lockFilePath = tempFolder.getRoot().toPath().resolve(DirectoryLock.LOCK_FILE_NAME); + lock.tryLock(); + assertThat(Files.exists(lockFilePath)).isTrue(); + assertThat(Files.isRegularFile(lockFilePath)).isTrue(); + + lock.stop(); + assertThat(Files.exists(lockFilePath)).isTrue(); + } + + @Test + public void tryLockConcurrently() { + exception.expect(IllegalStateException.class); + exception.expectMessage("Another SonarQube analysis is already in progress for this project"); + lock.tryLock(); + lock.tryLock(); + } + + @Test + /** + * If there is an error starting up the scan, we'll still try to unlock even if the lock + * was never done + */ + public void stopWithoutStarting() { + lock.stop(); + lock.stop(); + } + + @Test + public void tryLockTwice() { + lock.tryLock(); + lock.stop(); + lock.tryLock(); + lock.stop(); + } + + @Test + public void unLockWithNoLock() { + lock.stop(); + } + +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/ProjectReactorBuilderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/ProjectReactorBuilderTest.java new file mode 100644 index 00000000000..d1a61853ef2 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/ProjectReactorBuilderTest.java @@ -0,0 +1,683 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.scan; + +import com.google.common.collect.Maps; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.net.URL; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import org.apache.commons.io.FileUtils; +import org.apache.commons.lang.StringUtils; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.sonar.api.batch.AnalysisMode; +import org.sonar.api.batch.bootstrap.ProjectDefinition; +import org.sonar.api.batch.bootstrap.ProjectReactor; +import org.sonar.api.utils.MessageException; +import org.sonar.api.utils.log.LogTester; +import org.sonar.api.utils.log.LoggerLevel; +import org.sonar.batch.analysis.AnalysisProperties; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class ProjectReactorBuilderTest { + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Rule + public LogTester logTester = new LogTester(); + + private AnalysisMode mode; + + @Before + public void setUp() { + mode = mock(AnalysisMode.class); + } + + @Test + public void shouldDefineSimpleProject() { + ProjectDefinition projectDefinition = loadProjectDefinition("simple-project"); + + assertThat(projectDefinition.getKey()).isEqualTo("com.foo.project"); + assertThat(projectDefinition.getName()).isEqualTo("Foo Project"); + assertThat(projectDefinition.getVersion()).isEqualTo("1.0-SNAPSHOT"); + assertThat(projectDefinition.getDescription()).isEqualTo("Description of Foo Project"); + assertThat(projectDefinition.getSourceDirs()).contains("sources"); + } + + @Test + public void shouldFailIfUnexistingSourceDirectory() { + thrown.expect(MessageException.class); + thrown.expectMessage("The folder 'unexisting-source-dir' does not exist for 'com.foo.project' (base directory = " + + getResource(this.getClass(), "simple-project-with-unexisting-source-dir") + ")"); + + loadProjectDefinition("simple-project-with-unexisting-source-dir"); + } + + @Test + public void fail_if_sources_not_set() { + thrown.expect(MessageException.class); + thrown.expectMessage("You must define the following mandatory properties for 'com.foo.project': sonar.sources"); + loadProjectDefinition("simple-project-with-missing-source-dir"); + } + + @Test + public void shouldNotFailIfBlankSourceDirectory() { + loadProjectDefinition("simple-project-with-blank-source-dir"); + } + + @Test + public void modulesDuplicateIds() { + thrown.expect(MessageException.class); + thrown.expectMessage("Two modules have the same id: 'module1'. Each module must have a unique id."); + + loadProjectDefinition("multi-module-duplicate-id"); + } + + @Test + public void modulesRepeatedIds() { + ProjectDefinition rootProject = loadProjectDefinition("multi-module-repeated-id"); + + List<ProjectDefinition> modules = rootProject.getSubProjects(); + assertThat(modules.size()).isEqualTo(1); + // Module 1 + ProjectDefinition module1 = modules.get(0); + assertThat(module1.getKey()).isEqualTo("com.foo.project:module1"); + assertThat(module1.getName()).isEqualTo("Foo Module 1"); + + // Module 1 -> Module 1 + ProjectDefinition module1_module1 = module1.getSubProjects().get(0); + assertThat(module1_module1.getKey()).isEqualTo("com.foo.project:module1:module1"); + assertThat(module1_module1.getName()).isEqualTo("Foo Sub Module 1"); + } + + @Test + public void shouldDefineMultiModuleProjectWithDefinitionsAllInRootProject() throws IOException { + ProjectDefinition rootProject = loadProjectDefinition("multi-module-definitions-all-in-root"); + + // CHECK ROOT + assertThat(rootProject.getKey()).isEqualTo("com.foo.project"); + assertThat(rootProject.getName()).isEqualTo("Foo Project"); + assertThat(rootProject.getVersion()).isEqualTo("1.0-SNAPSHOT"); + assertThat(rootProject.getDescription()).isEqualTo("Description of Foo Project"); + // root project must not contain some properties - even if they are defined in the root properties file + assertThat(rootProject.getSourceDirs().contains("sources")).isFalse(); + assertThat(rootProject.getTestDirs().contains("tests")).isFalse(); + // and module properties must have been cleaned + assertThat(rootProject.properties().get("module1.sonar.projectKey")).isNull(); + assertThat(rootProject.properties().get("module2.sonar.projectKey")).isNull(); + // Check baseDir and workDir + assertThat(rootProject.getBaseDir().getCanonicalFile()) + .isEqualTo(getResource(this.getClass(), "multi-module-definitions-all-in-root")); + assertThat(rootProject.getWorkDir().getCanonicalFile()) + .isEqualTo(new File(getResource(this.getClass(), "multi-module-definitions-all-in-root"), ".sonar")); + + // CHECK MODULES + List<ProjectDefinition> modules = rootProject.getSubProjects(); + assertThat(modules.size()).isEqualTo(2); + + // Module 1 + ProjectDefinition module1 = modules.get(0); + assertThat(module1.getBaseDir().getCanonicalFile()).isEqualTo(getResource(this.getClass(), "multi-module-definitions-all-in-root/module1")); + assertThat(module1.getKey()).isEqualTo("com.foo.project:module1"); + assertThat(module1.getName()).isEqualTo("module1"); + assertThat(module1.getVersion()).isEqualTo("1.0-SNAPSHOT"); + // Description should not be inherited from parent if not set + assertThat(module1.getDescription()).isNull(); + assertThat(module1.getSourceDirs()).contains("sources"); + assertThat(module1.getTestDirs()).contains("tests"); + assertThat(module1.getBinaries()).contains("target/classes"); + // and module properties must have been cleaned + assertThat(module1.properties().get("module1.sonar.projectKey")).isNull(); + assertThat(module1.properties().get("module2.sonar.projectKey")).isNull(); + // Check baseDir and workDir + assertThat(module1.getBaseDir().getCanonicalFile()) + .isEqualTo(getResource(this.getClass(), "multi-module-definitions-all-in-root/module1")); + assertThat(module1.getWorkDir().getCanonicalFile()) + .isEqualTo(new File(getResource(this.getClass(), "multi-module-definitions-all-in-root"), ".sonar/com.foo.project_module1")); + + // Module 2 + ProjectDefinition module2 = modules.get(1); + assertThat(module2.getBaseDir().getCanonicalFile()).isEqualTo(getResource(this.getClass(), "multi-module-definitions-all-in-root/module2")); + assertThat(module2.getKey()).isEqualTo("com.foo.project:com.foo.project.module2"); + assertThat(module2.getName()).isEqualTo("Foo Module 2"); + assertThat(module2.getVersion()).isEqualTo("1.0-SNAPSHOT"); + assertThat(module2.getDescription()).isEqualTo("Description of Module 2"); + assertThat(module2.getSourceDirs()).contains("src"); + assertThat(module2.getTestDirs()).contains("tests"); + assertThat(module2.getBinaries()).contains("target/classes"); + // and module properties must have been cleaned + assertThat(module2.properties().get("module1.sonar.projectKey")).isNull(); + assertThat(module2.properties().get("module2.sonar.projectKey")).isNull(); + // Check baseDir and workDir + assertThat(module2.getBaseDir().getCanonicalFile()) + .isEqualTo(getResource(this.getClass(), "multi-module-definitions-all-in-root/module2")); + assertThat(module2.getWorkDir().getCanonicalFile()) + .isEqualTo(new File(getResource(this.getClass(), "multi-module-definitions-all-in-root"), ".sonar/com.foo.project_com.foo.project.module2")); + } + + // SONAR-4876 + @Test + public void shouldDefineMultiModuleProjectWithModuleKey() { + ProjectDefinition rootProject = loadProjectDefinition("multi-module-definitions-moduleKey"); + + // CHECK ROOT + // module properties must have been cleaned + assertThat(rootProject.properties().get("module1.sonar.moduleKey")).isNull(); + assertThat(rootProject.properties().get("module2.sonar.moduleKey")).isNull(); + + // CHECK MODULES + List<ProjectDefinition> modules = rootProject.getSubProjects(); + assertThat(modules.size()).isEqualTo(2); + + // Module 2 + ProjectDefinition module2 = modules.get(1); + assertThat(module2.getKey()).isEqualTo("com.foo.project.module2"); + } + + // SONARPLUGINS-2421 + @Test + public void shouldDefineMultiLanguageProjectWithDefinitionsAllInRootProject() throws IOException { + ProjectDefinition rootProject = loadProjectDefinition("multi-language-definitions-all-in-root"); + + // CHECK ROOT + assertThat(rootProject.getKey()).isEqualTo("example"); + assertThat(rootProject.getName()).isEqualTo("Example"); + assertThat(rootProject.getVersion()).isEqualTo("1.0"); + + // CHECK MODULES + List<ProjectDefinition> modules = rootProject.getSubProjects(); + assertThat(modules.size()).isEqualTo(2); + + // Module 1 + ProjectDefinition module1 = modules.get(0); + assertThat(module1.getBaseDir().getCanonicalFile()).isEqualTo(getResource(this.getClass(), "multi-language-definitions-all-in-root")); + assertThat(module1.getSourceDirs()).contains("src/main/java"); + // and module properties must have been cleaned + assertThat(module1.getWorkDir().getCanonicalFile()) + .isEqualTo(new File(getResource(this.getClass(), "multi-language-definitions-all-in-root"), ".sonar/example_java-module")); + + // Module 2 + ProjectDefinition module2 = modules.get(1); + assertThat(module2.getBaseDir().getCanonicalFile()).isEqualTo(getResource(this.getClass(), "multi-language-definitions-all-in-root")); + assertThat(module2.getSourceDirs()).contains("src/main/groovy"); + // and module properties must have been cleaned + assertThat(module2.getWorkDir().getCanonicalFile()) + .isEqualTo(new File(getResource(this.getClass(), "multi-language-definitions-all-in-root"), ".sonar/example_groovy-module")); + } + + @Test + public void shouldDefineMultiModuleProjectWithBaseDir() { + ProjectDefinition rootProject = loadProjectDefinition("multi-module-with-basedir"); + List<ProjectDefinition> modules = rootProject.getSubProjects(); + assertThat(modules.size()).isEqualTo(1); + assertThat(modules.get(0).getKey()).isEqualTo("com.foo.project:com.foo.project.module1"); + } + + @Test + public void shouldFailIfUnexistingModuleBaseDir() { + thrown.expect(MessageException.class); + thrown.expectMessage("The base directory of the module 'module1' does not exist: " + + getResource(this.getClass(), "multi-module-with-unexisting-basedir").getAbsolutePath() + File.separator + "module1"); + + loadProjectDefinition("multi-module-with-unexisting-basedir"); + } + + @Test + public void shouldFailIfUnexistingSourceFolderInheritedInMultimodule() { + thrown.expect(MessageException.class); + thrown.expectMessage("The folder 'unexisting-source-dir' does not exist for 'com.foo.project:module1' (base directory = " + + getResource(this.getClass(), "multi-module-with-unexisting-source-dir").getAbsolutePath() + File.separator + "module1)"); + + loadProjectDefinition("multi-module-with-unexisting-source-dir"); + } + + @Test + public void shouldFailIfExplicitUnexistingTestFolder() { + thrown.expect(MessageException.class); + thrown.expectMessage("The folder 'tests' does not exist for 'com.foo.project' (base directory = " + + getResource(this.getClass(), "simple-project-with-unexisting-test-dir").getAbsolutePath()); + + loadProjectDefinition("simple-project-with-unexisting-test-dir"); + } + + @Test + public void shouldFailIfExplicitUnexistingTestFolderOnModule() { + thrown.expect(MessageException.class); + thrown.expectMessage("The folder 'tests' does not exist for 'module1' (base directory = " + + getResource(this.getClass(), "multi-module-with-explicit-unexisting-test-dir").getAbsolutePath() + File.separator + "module1)"); + + loadProjectDefinition("multi-module-with-explicit-unexisting-test-dir"); + } + + @Test + public void multiModuleProperties() { + ProjectDefinition projectDefinition = loadProjectDefinition("big-multi-module-definitions-all-in-root"); + + assertThat(projectDefinition.properties().get("module11.property")).isNull(); + assertThat(projectDefinition.properties().get("sonar.profile")).isEqualTo("Foo"); + ProjectDefinition module1 = null; + ProjectDefinition module2 = null; + for (ProjectDefinition prj : projectDefinition.getSubProjects()) { + if (prj.getKey().equals("com.foo.project:module1")) { + module1 = prj; + } else if (prj.getKey().equals("com.foo.project:module2")) { + module2 = prj; + } + } + assertThat(module1.properties().get("module11.property")).isNull(); + assertThat(module1.properties().get("property")).isNull(); + assertThat(module1.properties().get("sonar.profile")).isEqualTo("Foo"); + assertThat(module2.properties().get("module11.property")).isNull(); + assertThat(module2.properties().get("property")).isNull(); + assertThat(module2.properties().get("sonar.profile")).isEqualTo("Foo"); + + ProjectDefinition module11 = null; + ProjectDefinition module12 = null; + for (ProjectDefinition prj : module1.getSubProjects()) { + if (prj.getKey().equals("com.foo.project:module1:module11")) { + module11 = prj; + } else if (prj.getKey().equals("com.foo.project:module1:module12")) { + module12 = prj; + } + } + assertThat(module11.properties().get("module1.module11.property")).isNull(); + assertThat(module11.properties().get("module11.property")).isNull(); + assertThat(module11.properties().get("property")).isEqualTo("My module11 property"); + assertThat(module11.properties().get("sonar.profile")).isEqualTo("Foo"); + assertThat(module12.properties().get("module11.property")).isNull(); + assertThat(module12.properties().get("property")).isNull(); + assertThat(module12.properties().get("sonar.profile")).isEqualTo("Foo"); + } + + @Test + public void shouldRemoveModulePropertiesFromTaskProperties() { + Map<String, String> props = loadProps("big-multi-module-definitions-all-in-root"); + + AnalysisProperties taskProperties = new AnalysisProperties(props, null); + assertThat(taskProperties.property("module1.module11.property")).isEqualTo("My module11 property"); + + new ProjectReactorBuilder(taskProperties, mode).execute(); + + assertThat(taskProperties.property("module1.module11.property")).isNull(); + } + + @Test + public void shouldFailIfMandatoryPropertiesAreNotPresent() { + Map<String, String> props = new HashMap<>(); + props.put("foo1", "bla"); + props.put("foo4", "bla"); + + thrown.expect(MessageException.class); + thrown.expectMessage("You must define the following mandatory properties for 'Unknown': foo2, foo3"); + + ProjectReactorBuilder.checkMandatoryProperties(props, new String[] {"foo1", "foo2", "foo3"}); + } + + @Test + public void shouldFailIfMandatoryPropertiesAreNotPresentButWithProjectKey() { + Map<String, String> props = new HashMap<>(); + props.put("foo1", "bla"); + props.put("sonar.projectKey", "my-project"); + + thrown.expect(MessageException.class); + thrown.expectMessage("You must define the following mandatory properties for 'my-project': foo2, foo3"); + + ProjectReactorBuilder.checkMandatoryProperties(props, new String[] {"foo1", "foo2", "foo3"}); + } + + @Test + public void shouldNotFailIfMandatoryPropertiesArePresent() { + Map<String, String> props = new HashMap<>(); + props.put("foo1", "bla"); + props.put("foo4", "bla"); + + ProjectReactorBuilder.checkMandatoryProperties(props, new String[] {"foo1"}); + + // No exception should be thrown + } + + @Test + public void shouldGetRelativeFile() { + assertThat(ProjectReactorBuilder.resolvePath(getResource(this.getClass(), "/"), "shouldGetFile/foo.properties")) + .isEqualTo(getResource(this.getClass(), "shouldGetFile/foo.properties")); + } + + @Test + public void shouldGetAbsoluteFile() { + File file = getResource(this.getClass(), "shouldGetFile/foo.properties"); + + assertThat(ProjectReactorBuilder.resolvePath(getResource(this.getClass(), "/"), file.getAbsolutePath())) + .isEqualTo(file); + } + + @Test + public void shouldMergeParentProperties() { + // Use a random value to avoid VM optimization that would create constant String and make s1 and s2 the same object + int i = (int) Math.random() * 10; + String s1 = "value" + i; + String s2 = "value" + i; + Map<String, String> parentProps = new HashMap<>(); + parentProps.put("toBeMergeProps", "fooParent"); + parentProps.put("existingChildProp", "barParent"); + parentProps.put("duplicatedProp", s1); + parentProps.put("sonar.projectDescription", "Desc from Parent"); + + Map<String, String> childProps = new HashMap<>(); + childProps.put("existingChildProp", "barChild"); + childProps.put("otherProp", "tutuChild"); + childProps.put("duplicatedProp", s2); + + ProjectReactorBuilder.mergeParentProperties(childProps, parentProps); + + assertThat(childProps).hasSize(4); + assertThat(childProps.get("toBeMergeProps")).isEqualTo("fooParent"); + assertThat(childProps.get("existingChildProp")).isEqualTo("barChild"); + assertThat(childProps.get("otherProp")).isEqualTo("tutuChild"); + assertThat(childProps.get("sonar.projectDescription")).isNull(); + assertThat(childProps.get("duplicatedProp")).isSameAs(parentProps.get("duplicatedProp")); + } + + @Test + public void shouldInitRootWorkDir() { + ProjectReactorBuilder builder = new ProjectReactorBuilder(new AnalysisProperties(Maps.<String, String>newHashMap(), null), mode); + File baseDir = new File("target/tmp/baseDir"); + + File workDir = builder.initRootProjectWorkDir(baseDir, Maps.<String, String>newHashMap()); + + assertThat(workDir).isEqualTo(new File(baseDir, ".sonar")); + } + + @Test + public void nonAssociatedMode() { + when(mode.isIssues()).thenReturn(true); + ProjectDefinition project = loadProjectDefinition("multi-module-with-basedir-not-associated"); + + assertThat(project.getKey()).isEqualTo("project"); + } + + @Test + public void shouldInitWorkDirWithCustomRelativeFolder() { + Map<String, String> props = Maps.<String, String>newHashMap(); + props.put("sonar.working.directory", ".foo"); + ProjectReactorBuilder builder = new ProjectReactorBuilder(new AnalysisProperties(props, null), mode); + File baseDir = new File("target/tmp/baseDir"); + + File workDir = builder.initRootProjectWorkDir(baseDir, props); + + assertThat(workDir).isEqualTo(new File(baseDir, ".foo")); + } + + @Test + public void shouldInitRootWorkDirWithCustomAbsoluteFolder() { + Map<String, String> props = Maps.<String, String>newHashMap(); + props.put("sonar.working.directory", new File("src").getAbsolutePath()); + ProjectReactorBuilder builder = new ProjectReactorBuilder(new AnalysisProperties(props, null), mode); + File baseDir = new File("target/tmp/baseDir"); + + File workDir = builder.initRootProjectWorkDir(baseDir, props); + + assertThat(workDir).isEqualTo(new File("src").getAbsoluteFile()); + } + + @Test + public void shouldFailIf2ModulesWithSameKey() { + Map<String, String> props = new HashMap<>(); + props.put("sonar.projectKey", "root"); + ProjectDefinition root = ProjectDefinition.create().setProperties(props); + + Map<String, String> props1 = new HashMap<>(); + props1.put("sonar.projectKey", "mod1"); + root.addSubProject(ProjectDefinition.create().setProperties(props1)); + + // Check uniqueness of a new module: OK + Map<String, String> props2 = new HashMap<>(); + props2.put("sonar.projectKey", "mod2"); + ProjectDefinition mod2 = ProjectDefinition.create().setProperties(props2); + ProjectReactorBuilder.checkUniquenessOfChildKey(mod2, root); + + // Now, add it and check again + root.addSubProject(mod2); + + thrown.expect(MessageException.class); + thrown.expectMessage("Project 'root' can't have 2 modules with the following key: mod2"); + + ProjectReactorBuilder.checkUniquenessOfChildKey(mod2, root); + } + + @Test + public void shouldSetModuleKeyIfNotPresent() { + Map<String, String> props = new HashMap<>(); + props.put("sonar.projectVersion", "1.0"); + + // should be set + ProjectReactorBuilder.setModuleKeyAndNameIfNotDefined(props, "foo", "parent"); + assertThat(props.get("sonar.moduleKey")).isEqualTo("parent:foo"); + assertThat(props.get("sonar.projectName")).isEqualTo("foo"); + + // but not this 2nd time + ProjectReactorBuilder.setModuleKeyAndNameIfNotDefined(props, "bar", "parent"); + assertThat(props.get("sonar.moduleKey")).isEqualTo("parent:foo"); + assertThat(props.get("sonar.projectName")).isEqualTo("foo"); + } + + private ProjectDefinition loadProjectDefinition(String projectFolder) { + Map<String, String> props = loadProps(projectFolder); + AnalysisProperties bootstrapProps = new AnalysisProperties(props, null); + ProjectReactor projectReactor = new ProjectReactorBuilder(bootstrapProps, mode).execute(); + return projectReactor.getRoot(); + } + + protected static Properties toProperties(File propertyFile) { + Properties propsFromFile = new Properties(); + try (FileInputStream fileInputStream = new FileInputStream(propertyFile)) { + propsFromFile.load(fileInputStream); + } catch (IOException e) { + throw new IllegalStateException("Impossible to read the property file: " + propertyFile.getAbsolutePath(), e); + } + // Trim properties + for (String propKey : propsFromFile.stringPropertyNames()) { + propsFromFile.setProperty(propKey, StringUtils.trim(propsFromFile.getProperty(propKey))); + } + return propsFromFile; + } + + private Map<String, String> loadProps(String projectFolder) { + Map<String, String> props = Maps.<String, String>newHashMap(); + Properties runnerProps = toProperties(getResource(this.getClass(), projectFolder + "/sonar-project.properties")); + for (final String name : runnerProps.stringPropertyNames()) { + props.put(name, runnerProps.getProperty(name)); + } + props.put("sonar.projectBaseDir", getResource(this.getClass(), projectFolder).getAbsolutePath()); + return props; + } + + public Map<String, String> toMap(Properties props) { + Map<String, String> result = new HashMap<>(); + for (Map.Entry<Object, Object> entry : props.entrySet()) { + result.put(entry.getKey().toString(), entry.getValue().toString()); + } + return result; + } + + @Test + public void shouldGetList() { + Map<String, String> props = new HashMap<>(); + + props.put("prop", " foo ,, bar , \n\ntoto,tutu"); + assertThat(ProjectReactorBuilder.getListFromProperty(props, "prop")).containsOnly("foo", "bar", "toto", "tutu"); + } + + @Test + public void shouldGetEmptyList() { + Map<String, String> props = new HashMap<>(); + + props.put("prop", ""); + assertThat(ProjectReactorBuilder.getListFromProperty(props, "prop")).isEmpty(); + } + + @Test + public void shouldGetListFromFile() throws IOException { + String filePath = "shouldGetList/foo.properties"; + Map<String, String> props = loadPropsFromFile(filePath); + + assertThat(ProjectReactorBuilder.getListFromProperty(props, "prop")).containsOnly("foo", "bar", "toto", "tutu"); + } + + @Test + public void shouldDefineProjectWithBuildDir() { + ProjectDefinition rootProject = loadProjectDefinition("simple-project-with-build-dir"); + File buildDir = rootProject.getBuildDir(); + assertThat(buildDir).isDirectory().exists(); + assertThat(new File(buildDir, "report.txt")).isFile().exists(); + assertThat(buildDir.getName()).isEqualTo("build"); + } + + @Test + public void doNotMixPropertiesWhenModuleKeyIsPrefixOfAnother() throws IOException { + ProjectDefinition rootProject = loadProjectDefinition("multi-module-definitions-same-prefix"); + + // CHECK ROOT + assertThat(rootProject.getKey()).isEqualTo("com.foo.project"); + assertThat(rootProject.getName()).isEqualTo("Foo Project"); + assertThat(rootProject.getVersion()).isEqualTo("1.0-SNAPSHOT"); + assertThat(rootProject.getDescription()).isEqualTo("Description of Foo Project"); + // root project must not contain some properties - even if they are defined in the root properties file + assertThat(rootProject.getSourceDirs().contains("sources")).isFalse(); + assertThat(rootProject.getTestDirs().contains("tests")).isFalse(); + // and module properties must have been cleaned + assertThat(rootProject.properties().get("module1.sonar.projectKey")).isNull(); + assertThat(rootProject.properties().get("module2.sonar.projectKey")).isNull(); + // Check baseDir and workDir + assertThat(rootProject.getBaseDir().getCanonicalFile()) + .isEqualTo(getResource(this.getClass(), "multi-module-definitions-same-prefix")); + assertThat(rootProject.getWorkDir().getCanonicalFile()) + .isEqualTo(new File(getResource(this.getClass(), "multi-module-definitions-same-prefix"), ".sonar")); + + // CHECK MODULES + List<ProjectDefinition> modules = rootProject.getSubProjects(); + assertThat(modules.size()).isEqualTo(2); + + // Module 1 + ProjectDefinition module1 = modules.get(0); + assertThat(module1.getBaseDir().getCanonicalFile()).isEqualTo(getResource(this.getClass(), "multi-module-definitions-same-prefix/module1")); + assertThat(module1.getKey()).isEqualTo("com.foo.project:module1"); + assertThat(module1.getName()).isEqualTo("module1"); + assertThat(module1.getVersion()).isEqualTo("1.0-SNAPSHOT"); + // Description should not be inherited from parent if not set + assertThat(module1.getDescription()).isNull(); + assertThat(module1.getSourceDirs()).contains("sources"); + assertThat(module1.getTestDirs()).contains("tests"); + assertThat(module1.getBinaries()).contains("target/classes"); + // and module properties must have been cleaned + assertThat(module1.properties().get("module1.sonar.projectKey")).isNull(); + assertThat(module1.properties().get("module2.sonar.projectKey")).isNull(); + // Check baseDir and workDir + assertThat(module1.getBaseDir().getCanonicalFile()) + .isEqualTo(getResource(this.getClass(), "multi-module-definitions-same-prefix/module1")); + assertThat(module1.getWorkDir().getCanonicalFile()) + .isEqualTo(new File(getResource(this.getClass(), "multi-module-definitions-same-prefix"), ".sonar/com.foo.project_module1")); + + // Module 1 Feature + ProjectDefinition module1Feature = modules.get(1); + assertThat(module1Feature.getBaseDir().getCanonicalFile()).isEqualTo(getResource(this.getClass(), "multi-module-definitions-same-prefix/module1.feature")); + assertThat(module1Feature.getKey()).isEqualTo("com.foo.project:com.foo.project.module1.feature"); + assertThat(module1Feature.getName()).isEqualTo("Foo Module 1 Feature"); + assertThat(module1Feature.getVersion()).isEqualTo("1.0-SNAPSHOT"); + assertThat(module1Feature.getDescription()).isEqualTo("Description of Module 1 Feature"); + assertThat(module1Feature.getSourceDirs()).contains("src"); + assertThat(module1Feature.getTestDirs()).contains("tests"); + assertThat(module1Feature.getBinaries()).contains("target/classes"); + // and module properties must have been cleaned + assertThat(module1Feature.properties().get("module1.sonar.projectKey")).isNull(); + assertThat(module1Feature.properties().get("module2.sonar.projectKey")).isNull(); + // Check baseDir and workDir + assertThat(module1Feature.getBaseDir().getCanonicalFile()) + .isEqualTo(getResource(this.getClass(), "multi-module-definitions-same-prefix/module1.feature")); + assertThat(module1Feature.getWorkDir().getCanonicalFile()) + .isEqualTo(new File(getResource(this.getClass(), "multi-module-definitions-same-prefix"), ".sonar/com.foo.project_com.foo.project.module1.feature")); + } + + @Test + public void should_log_a_warning_when_a_dropped_property_is_present() { + Map<String, String> props = loadProps("simple-project"); + props.put("sonar.qualitygate", "somevalue"); + AnalysisProperties bootstrapProps = new AnalysisProperties(props, null); + new ProjectReactorBuilder(bootstrapProps, mode).execute(); + + assertThat(logTester.logs(LoggerLevel.WARN)).containsOnly("Property 'sonar.qualitygate' is not supported any more. It will be ignored."); + } + + private Map<String, String> loadPropsFromFile(String filePath) throws IOException { + Properties props = new Properties(); + try (FileInputStream fileInputStream = new FileInputStream(getResource(this.getClass(), filePath))) { + props.load(fileInputStream); + } + Map<String, String> result = new HashMap<>(); + for (Map.Entry<Object, Object> entry : props.entrySet()) { + result.put(entry.getKey().toString(), entry.getValue().toString()); + } + return result; + } + + /** + * Search for a test resource in the classpath. For example getResource("org/sonar/MyClass/foo.txt"); + * + * @param path the starting slash is optional + * @return the resource. Null if resource not found + */ + public static File getResource(String path) { + String resourcePath = path; + if (!resourcePath.startsWith("/")) { + resourcePath = "/" + resourcePath; + } + URL url = ProjectReactorBuilderTest.class.getResource(resourcePath); + if (url != null) { + return FileUtils.toFile(url); + } + return null; + } + + /** + * Search for a resource in the classpath. For example calling the method getResource(getClass(), "myTestName/foo.txt") from + * the class org.sonar.Foo loads the file $basedir/src/test/resources/org/sonar/Foo/myTestName/foo.txt + * + * @return the resource. Null if resource not found + */ + public static File getResource(Class baseClass, String path) { + String resourcePath = StringUtils.replaceChars(baseClass.getCanonicalName(), '.', '/'); + if (!path.startsWith("/")) { + resourcePath += "/"; + } + resourcePath += path; + return getResource(resourcePath); + } + +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/ProjectReactorValidatorTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/ProjectReactorValidatorTest.java new file mode 100644 index 00000000000..fd5882467b5 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/ProjectReactorValidatorTest.java @@ -0,0 +1,185 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.scan; + +import static org.mockito.Mockito.when; + +import org.sonar.api.utils.MessageException; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.sonar.api.CoreProperties; +import org.sonar.api.batch.bootstrap.ProjectDefinition; +import org.sonar.api.batch.bootstrap.ProjectReactor; +import org.sonar.api.config.Settings; +import org.sonar.batch.analysis.DefaultAnalysisMode; +import static org.mockito.Mockito.mock; + +public class ProjectReactorValidatorTest { + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + private ProjectReactorValidator validator; + private Settings settings; + private DefaultAnalysisMode mode; + + @Before + public void prepare() { + mode = mock(DefaultAnalysisMode.class); + settings = new Settings(); + validator = new ProjectReactorValidator(settings, mode); + } + + @Test + public void not_fail_with_valid_key() { + validator.validate(createProjectReactor("foo")); + validator.validate(createProjectReactor("123foo")); + validator.validate(createProjectReactor("foo123")); + validator.validate(createProjectReactor("1Z3")); + validator.validate(createProjectReactor("a123")); + validator.validate(createProjectReactor("123a")); + validator.validate(createProjectReactor("1:2")); + validator.validate(createProjectReactor("3-3")); + validator.validate(createProjectReactor("-:")); + } + + @Test + public void allow_slash_issues_mode() { + when(mode.isIssues()).thenReturn(true); + validator.validate(createProjectReactor("project/key")); + + when(mode.isIssues()).thenReturn(false); + thrown.expect(MessageException.class); + thrown.expectMessage("is not a valid project or module key"); + validator.validate(createProjectReactor("project/key")); + } + + @Test + public void not_fail_with_alphanumeric_key() { + ProjectReactor reactor = createProjectReactor("Foobar2"); + validator.validate(reactor); + } + + @Test + public void should_not_fail_with_dot_key() { + ProjectReactor reactor = createProjectReactor("foo.bar"); + validator.validate(reactor); + } + + @Test + public void not_fail_with_dash_key() { + ProjectReactor reactor = createProjectReactor("foo-bar"); + validator.validate(reactor); + } + + @Test + public void not_fail_with_colon_key() { + ProjectReactor reactor = createProjectReactor("foo:bar"); + validator.validate(reactor); + } + + @Test + public void not_fail_with_underscore_key() { + ProjectReactor reactor = createProjectReactor("foo_bar"); + validator.validate(reactor); + } + + @Test + public void fail_with_invalid_key() { + ProjectReactor reactor = createProjectReactor("foo$bar"); + + thrown.expect(MessageException.class); + thrown.expectMessage("\"foo$bar\" is not a valid project or module key"); + validator.validate(reactor); + } + + @Test + public void fail_with_backslash_in_key() { + ProjectReactor reactor = createProjectReactor("foo\\bar"); + + thrown.expect(MessageException.class); + thrown.expectMessage("\"foo\\bar\" is not a valid project or module key"); + validator.validate(reactor); + } + + @Test + public void not_fail_with_valid_branch() { + validator.validate(createProjectReactor("foo", "branch")); + validator.validate(createProjectReactor("foo", "Branch2")); + validator.validate(createProjectReactor("foo", "bra.nch")); + validator.validate(createProjectReactor("foo", "bra-nch")); + validator.validate(createProjectReactor("foo", "1")); + validator.validate(createProjectReactor("foo", "bra_nch")); + } + + @Test + public void fail_with_invalid_branch() { + ProjectReactor reactor = createProjectReactor("foo", "bran#ch"); + thrown.expect(MessageException.class); + thrown.expectMessage("\"bran#ch\" is not a valid branch name"); + validator.validate(reactor); + } + + @Test + public void fail_with_colon_in_branch() { + ProjectReactor reactor = createProjectReactor("foo", "bran:ch"); + thrown.expect(MessageException.class); + thrown.expectMessage("\"bran:ch\" is not a valid branch name"); + validator.validate(reactor); + } + + @Test + public void fail_with_only_digits() { + ProjectReactor reactor = createProjectReactor("12345"); + + thrown.expect(MessageException.class); + thrown.expectMessage("\"12345\" is not a valid project or module key"); + validator.validate(reactor); + } + + @Test + public void fail_with_deprecated_sonar_phase() { + ProjectReactor reactor = createProjectReactor("foo"); + settings.setProperty("sonar.phase", "phase"); + + thrown.expect(MessageException.class); + thrown.expectMessage("\"sonar.phase\" is deprecated"); + validator.validate(reactor); + } + + private ProjectReactor createProjectReactor(String projectKey) { + ProjectDefinition def = ProjectDefinition.create().setProperty(CoreProperties.PROJECT_KEY_PROPERTY, projectKey); + ProjectReactor reactor = new ProjectReactor(def); + return reactor; + } + + private ProjectReactor createProjectReactor(String projectKey, String branch) { + ProjectDefinition def = ProjectDefinition.create() + .setProperty(CoreProperties.PROJECT_KEY_PROPERTY, projectKey) + .setProperty(CoreProperties.PROJECT_BRANCH_PROPERTY, branch); + ProjectReactor reactor = new ProjectReactor(def); + settings.setProperty(CoreProperties.PROJECT_BRANCH_PROPERTY, branch); + return reactor; + } + +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/ProjectScanContainerTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/ProjectScanContainerTest.java new file mode 100644 index 00000000000..512462923a1 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/ProjectScanContainerTest.java @@ -0,0 +1,63 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.scan; + +import org.junit.Test; +import org.sonar.api.BatchExtension; +import org.sonar.api.ServerExtension; +import org.sonar.api.batch.InstantiationStrategy; +import org.sonar.api.task.TaskExtension; + +import static org.assertj.core.api.Assertions.assertThat; + +public class ProjectScanContainerTest { + + @Test + public void should_add_only_batch_extensions() { + ProjectScanContainer.BatchExtensionFilter filter = new ProjectScanContainer.BatchExtensionFilter(); + + assertThat(filter.accept(new MyBatchExtension())).isTrue(); + assertThat(filter.accept(MyBatchExtension.class)).isTrue(); + + assertThat(filter.accept(new MyProjectExtension())).isFalse(); + assertThat(filter.accept(MyProjectExtension.class)).isFalse(); + assertThat(filter.accept(new MyServerExtension())).isFalse(); + assertThat(filter.accept(MyServerExtension.class)).isFalse(); + assertThat(filter.accept(new MyTaskExtension())).isFalse(); + assertThat(filter.accept(MyTaskExtension.class)).isFalse(); + } + + @InstantiationStrategy(InstantiationStrategy.PER_BATCH) + static class MyBatchExtension implements BatchExtension { + + } + + static class MyProjectExtension implements BatchExtension { + + } + + static class MyServerExtension implements ServerExtension { + + } + + static class MyTaskExtension implements TaskExtension { + + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/ProjectSettingsTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/ProjectSettingsTest.java new file mode 100644 index 00000000000..7f9c6eecc4c --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/ProjectSettingsTest.java @@ -0,0 +1,142 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.scan; + +import com.google.common.collect.HashBasedTable; +import com.google.common.collect.ImmutableTable; +import com.google.common.collect.Table; +import java.util.Collections; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.sonar.api.CoreProperties; +import org.sonar.api.batch.bootstrap.ProjectDefinition; +import org.sonar.api.batch.bootstrap.ProjectReactor; +import org.sonar.api.config.PropertyDefinitions; +import org.sonar.api.utils.MessageException; +import org.sonar.api.utils.log.LogTester; +import org.sonar.batch.analysis.DefaultAnalysisMode; +import org.sonar.batch.bootstrap.GlobalMode; +import org.sonar.batch.bootstrap.GlobalProperties; +import org.sonar.batch.bootstrap.GlobalSettings; +import org.sonar.batch.repository.FileData; +import org.sonar.batch.repository.ProjectRepositories; +import org.sonar.scanner.protocol.input.GlobalRepositories; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class ProjectSettingsTest { + + @Rule + public ExpectedException thrown = ExpectedException.none(); + @Rule + public LogTester logTester = new LogTester(); + + private ProjectRepositories projectRef; + private ProjectDefinition project; + private GlobalSettings bootstrapProps; + private Table<String, String, FileData> emptyFileData; + private Table<String, String, String> emptySettings; + + private GlobalMode globalMode; + private DefaultAnalysisMode mode; + + @Before + public void prepare() { + emptyFileData = ImmutableTable.of(); + emptySettings = ImmutableTable.of(); + project = ProjectDefinition.create().setKey("struts"); + globalMode = mock(GlobalMode.class); + mode = mock(DefaultAnalysisMode.class); + bootstrapProps = new GlobalSettings(new GlobalProperties(Collections.<String, String>emptyMap()), new PropertyDefinitions(), new GlobalRepositories(), globalMode); + } + + @Test + public void should_load_project_props() { + project.setProperty("project.prop", "project"); + + projectRef = new ProjectRepositories(emptySettings, emptyFileData, null); + ProjectSettings batchSettings = new ProjectSettings(new ProjectReactor(project), bootstrapProps, new PropertyDefinitions(), projectRef, mode); + + assertThat(batchSettings.getString("project.prop")).isEqualTo("project"); + } + + @Test + public void should_load_project_root_settings() { + Table<String, String, String> settings = HashBasedTable.create(); + settings.put("struts", "sonar.cpd.cross", "true"); + settings.put("struts", "sonar.java.coveragePlugin", "jacoco"); + + projectRef = new ProjectRepositories(settings, emptyFileData, null); + ProjectSettings batchSettings = new ProjectSettings(new ProjectReactor(project), bootstrapProps, new PropertyDefinitions(), projectRef, mode); + assertThat(batchSettings.getString("sonar.java.coveragePlugin")).isEqualTo("jacoco"); + } + + @Test + public void should_load_project_root_settings_on_branch() { + project.setProperty(CoreProperties.PROJECT_BRANCH_PROPERTY, "mybranch"); + + Table<String, String, String> settings = HashBasedTable.create(); + settings.put("struts:mybranch", "sonar.cpd.cross", "true"); + settings.put("struts:mybranch", "sonar.java.coveragePlugin", "jacoco"); + + projectRef = new ProjectRepositories(settings, emptyFileData, null); + + ProjectSettings batchSettings = new ProjectSettings(new ProjectReactor(project), bootstrapProps, new PropertyDefinitions(), projectRef, mode); + + assertThat(batchSettings.getString("sonar.java.coveragePlugin")).isEqualTo("jacoco"); + } + + @Test + public void should_not_fail_when_accessing_secured_properties() { + Table<String, String, String> settings = HashBasedTable.create(); + settings.put("struts", "sonar.foo.secured", "bar"); + settings.put("struts", "sonar.foo.license.secured", "bar2"); + + projectRef = new ProjectRepositories(settings, emptyFileData, null); + ProjectSettings batchSettings = new ProjectSettings(new ProjectReactor(project), bootstrapProps, new PropertyDefinitions(), projectRef, mode); + + assertThat(batchSettings.getString("sonar.foo.license.secured")).isEqualTo("bar2"); + assertThat(batchSettings.getString("sonar.foo.secured")).isEqualTo("bar"); + } + + @Test + public void should_fail_when_accessing_secured_properties_in_issues_mode() { + Table<String, String, String> settings = HashBasedTable.create(); + settings.put("struts", "sonar.foo.secured", "bar"); + settings.put("struts", "sonar.foo.license.secured", "bar2"); + + when(mode.isIssues()).thenReturn(true); + + projectRef = new ProjectRepositories(settings, emptyFileData, null); + ProjectSettings batchSettings = new ProjectSettings(new ProjectReactor(project), bootstrapProps, new PropertyDefinitions(), projectRef, mode); + + assertThat(batchSettings.getString("sonar.foo.license.secured")).isEqualTo("bar2"); + thrown.expect(MessageException.class); + thrown + .expectMessage( + "Access to the secured property 'sonar.foo.secured' is not possible in issues mode. The SonarQube plugin which requires this property must be deactivated in issues mode."); + batchSettings.getString("sonar.foo.secured"); + } + +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/WorkDirectoryCleanerTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/WorkDirectoryCleanerTest.java new file mode 100644 index 00000000000..8bd3d5c4e3a --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/WorkDirectoryCleanerTest.java @@ -0,0 +1,79 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.scan; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.sonar.api.batch.bootstrap.ProjectDefinition; +import org.sonar.api.batch.bootstrap.ProjectReactor; +import org.sonar.home.cache.DirectoryLock; + +import java.io.File; +import java.io.IOException; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class WorkDirectoryCleanerTest { + private WorkDirectoryCleaner cleaner; + @Rule + public TemporaryFolder temp = new TemporaryFolder(); + + @Before + public void setUp() throws IOException { + // create files to clean + temp.newFile(); + File newFolder = temp.newFolder(); + File fileInFolder = new File(newFolder, "test"); + fileInFolder.createNewFile(); + + File lock = new File(temp.getRoot(), DirectoryLock.LOCK_FILE_NAME); + lock.createNewFile(); + + // mock project + ProjectReactor projectReactor = mock(ProjectReactor.class); + ProjectDefinition projectDefinition = mock(ProjectDefinition.class); + when(projectReactor.getRoot()).thenReturn(projectDefinition); + when(projectDefinition.getWorkDir()).thenReturn(temp.getRoot()); + + assertThat(temp.getRoot().list().length).isGreaterThan(1); + cleaner = new WorkDirectoryCleaner(projectReactor); + } + + @Test + public void testNonExisting() { + temp.delete(); + cleaner.execute(); + } + + @Test + public void testClean() { + File lock = new File(temp.getRoot(), DirectoryLock.LOCK_FILE_NAME); + cleaner.execute(); + + assertThat(temp.getRoot()).exists(); + assertThat(lock).exists(); + assertThat(temp.getRoot().list()).containsOnly(DirectoryLock.LOCK_FILE_NAME); + } + +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/filesystem/AdditionalFilePredicatesTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/filesystem/AdditionalFilePredicatesTest.java new file mode 100644 index 00000000000..c0f9fd3c062 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/filesystem/AdditionalFilePredicatesTest.java @@ -0,0 +1,45 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.scan.filesystem; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.sonar.api.batch.fs.FilePredicate; +import org.sonar.api.batch.fs.internal.DefaultInputFile; + +import static org.assertj.core.api.Assertions.assertThat; + +public class AdditionalFilePredicatesTest { + + @Rule + public TemporaryFolder temp = new TemporaryFolder(); + + @Test + public void key() { + FilePredicate predicate = new AdditionalFilePredicates.KeyPredicate("struts:Action.java"); + + DefaultInputFile inputFile = new DefaultInputFile("struts", "Action.java"); + assertThat(predicate.apply(inputFile)).isTrue(); + + inputFile = new DefaultInputFile("struts", "Filter.java"); + assertThat(predicate.apply(inputFile)).isFalse(); + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/filesystem/ComponentIndexerTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/filesystem/ComponentIndexerTest.java new file mode 100644 index 00000000000..554f175695f --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/filesystem/ComponentIndexerTest.java @@ -0,0 +1,141 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.scan.filesystem; + +import org.sonar.api.batch.fs.InputFile.Status; + +import org.sonar.batch.analysis.DefaultAnalysisMode; + +import java.io.File; +import java.io.IOException; + +import org.apache.commons.io.FileUtils; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.mockito.ArgumentMatcher; +import org.sonar.api.batch.SonarIndex; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.fs.internal.DefaultFileSystem; +import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.resources.AbstractLanguage; +import org.sonar.api.resources.Directory; +import org.sonar.api.resources.Java; +import org.sonar.api.resources.Languages; +import org.sonar.api.resources.Project; +import org.sonar.api.resources.Qualifiers; +import org.sonar.api.resources.Resource; +import org.sonar.batch.index.BatchComponent; +import org.sonar.batch.index.BatchComponentCache; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.argThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class ComponentIndexerTest { + + @Rule + public TemporaryFolder temp = new TemporaryFolder(); + private File baseDir; + private DefaultFileSystem fs; + private SonarIndex sonarIndex; + private AbstractLanguage cobolLanguage; + private Project project; + private ModuleFileSystemInitializer initializer; + private DefaultAnalysisMode mode; + + @Before + public void prepare() throws IOException { + baseDir = temp.newFolder(); + fs = new DefaultFileSystem(baseDir.toPath()); + sonarIndex = mock(SonarIndex.class); + project = new Project("myProject"); + initializer = mock(ModuleFileSystemInitializer.class); + mode = mock(DefaultAnalysisMode.class); + when(initializer.baseDir()).thenReturn(baseDir); + when(initializer.workingDir()).thenReturn(temp.newFolder()); + cobolLanguage = new AbstractLanguage("cobol") { + @Override + public String[] getFileSuffixes() { + return new String[] {"cbl"}; + } + }; + } + + @Test + public void should_index_java_files() throws IOException { + Languages languages = new Languages(Java.INSTANCE); + ComponentIndexer indexer = createIndexer(languages); + DefaultModuleFileSystem fs = new DefaultModuleFileSystem(project, null, mock(FileIndexer.class), initializer, indexer, mode); + fs.add(newInputFile("src/main/java/foo/bar/Foo.java", "", "foo/bar/Foo.java", "java", false, Status.ADDED)); + fs.add(newInputFile("src/main/java2/foo/bar/Foo.java", "", "foo/bar/Foo.java", "java", false, Status.ADDED)); + // should index even if filter is applied + fs.add(newInputFile("src/test/java/foo/bar/FooTest.java", "", "foo/bar/FooTest.java", "java", true, Status.SAME)); + + fs.index(); + + verify(sonarIndex).index(org.sonar.api.resources.File.create("src/main/java/foo/bar/Foo.java", Java.INSTANCE, false)); + verify(sonarIndex).index(org.sonar.api.resources.File.create("src/main/java2/foo/bar/Foo.java", Java.INSTANCE, false)); + verify(sonarIndex).index(argThat(new ArgumentMatcher<org.sonar.api.resources.File>() { + @Override + public boolean matches(Object arg0) { + org.sonar.api.resources.File javaFile = (org.sonar.api.resources.File) arg0; + return javaFile.getKey().equals("src/test/java/foo/bar/FooTest.java") + && javaFile.getPath().equals("src/test/java/foo/bar/FooTest.java") + && javaFile.getQualifier().equals(Qualifiers.UNIT_TEST_FILE); + } + })); + } + + private ComponentIndexer createIndexer(Languages languages) { + BatchComponentCache resourceCache = mock(BatchComponentCache.class); + when(resourceCache.get(any(Resource.class))) + .thenReturn(new BatchComponent(2, org.sonar.api.resources.File.create("foo.php"), new BatchComponent(1, Directory.create("src"), null))); + return new ComponentIndexer(project, languages, sonarIndex, resourceCache); + } + + @Test + public void should_index_cobol_files() throws IOException { + Languages languages = new Languages(cobolLanguage); + ComponentIndexer indexer = createIndexer(languages); + DefaultModuleFileSystem fs = new DefaultModuleFileSystem(project, null, mock(FileIndexer.class), initializer, indexer, mode); + fs.add(newInputFile("src/foo/bar/Foo.cbl", "", "foo/bar/Foo.cbl", "cobol", false, Status.ADDED)); + fs.add(newInputFile("src2/foo/bar/Foo.cbl", "", "foo/bar/Foo.cbl", "cobol", false, Status.ADDED)); + fs.add(newInputFile("src/test/foo/bar/FooTest.cbl", "", "foo/bar/FooTest.cbl", "cobol", true, Status.ADDED)); + + fs.index(); + + verify(sonarIndex).index(org.sonar.api.resources.File.create("/src/foo/bar/Foo.cbl", cobolLanguage, false)); + verify(sonarIndex).index(org.sonar.api.resources.File.create("/src2/foo/bar/Foo.cbl", cobolLanguage, false)); + verify(sonarIndex).index(org.sonar.api.resources.File.create("/src/test/foo/bar/FooTest.cbl", cobolLanguage, true)); + } + + private DefaultInputFile newInputFile(String path, String content, String sourceRelativePath, String languageKey, boolean unitTest, InputFile.Status status) throws IOException { + File file = new File(baseDir, path); + FileUtils.write(file, content); + return new DefaultInputFile("foo", path) + .setLanguage(languageKey) + .setType(unitTest ? InputFile.Type.TEST : InputFile.Type.MAIN) + .setStatus(status); + } + +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/filesystem/DefaultModuleFileSystemTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/filesystem/DefaultModuleFileSystemTest.java new file mode 100644 index 00000000000..46b70a17522 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/filesystem/DefaultModuleFileSystemTest.java @@ -0,0 +1,200 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.scan.filesystem; + +import org.sonar.api.batch.fs.InputFile.Status; + +import org.junit.Before; +import org.sonar.batch.analysis.DefaultAnalysisMode; +import com.google.common.collect.Lists; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.rules.TemporaryFolder; +import org.mockito.Mockito; +import org.sonar.api.CoreProperties; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.config.Settings; +import org.sonar.api.resources.Project; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.Charset; +import java.util.Arrays; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.when; + +public class DefaultModuleFileSystemTest { + + @Rule + public TemporaryFolder temp = new TemporaryFolder(); + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + private Settings settings; + private FileIndexer fileIndexer; + private ModuleFileSystemInitializer initializer; + private ComponentIndexer componentIndexer; + private ModuleInputFileCache moduleInputFileCache; + private DefaultAnalysisMode mode; + + @Before + public void setUp() { + settings = new Settings(); + fileIndexer = mock(FileIndexer.class); + initializer = mock(ModuleFileSystemInitializer.class, Mockito.RETURNS_DEEP_STUBS); + componentIndexer = mock(ComponentIndexer.class); + moduleInputFileCache = mock(ModuleInputFileCache.class); + mode = mock(DefaultAnalysisMode.class); + } + + @Test + public void test_equals_and_hashCode() throws Exception { + DefaultModuleFileSystem foo1 = new DefaultModuleFileSystem(moduleInputFileCache, + new Project("foo"), settings, fileIndexer, initializer, componentIndexer, mode); + DefaultModuleFileSystem foo2 = new DefaultModuleFileSystem(moduleInputFileCache, + new Project("foo"), settings, fileIndexer, initializer, componentIndexer, mode); + DefaultModuleFileSystem bar = new DefaultModuleFileSystem(moduleInputFileCache, + new Project("bar"), settings, fileIndexer, initializer, componentIndexer, mode); + DefaultModuleFileSystem branch = new DefaultModuleFileSystem(moduleInputFileCache, + new Project("bar", "branch", "My project"), settings, fileIndexer, initializer, componentIndexer, mode); + + assertThat(foo1.moduleKey()).isEqualTo("foo"); + assertThat(branch.moduleKey()).isEqualTo("bar:branch"); + assertThat(foo1.equals(foo1)).isTrue(); + assertThat(foo1.equals(foo2)).isTrue(); + assertThat(foo1.equals(bar)).isFalse(); + assertThat(foo1.equals("foo")).isFalse(); + assertThat(foo1.hashCode()).isEqualTo(foo1.hashCode()); + assertThat(foo1.hashCode()).isEqualTo(foo2.hashCode()); + } + + @Test + public void default_source_encoding() { + DefaultModuleFileSystem fs = new DefaultModuleFileSystem(moduleInputFileCache, + new Project("foo"), settings, fileIndexer, initializer, componentIndexer, mode); + + assertThat(fs.sourceCharset()).isEqualTo(Charset.defaultCharset()); + assertThat(fs.isDefaultJvmEncoding()).isTrue(); + } + + @Test + public void source_encoding_is_set() { + settings.setProperty(CoreProperties.ENCODING_PROPERTY, "Cp1124"); + DefaultModuleFileSystem fs = new DefaultModuleFileSystem(moduleInputFileCache, + new Project("foo"), settings, fileIndexer, initializer, componentIndexer, mode); + + assertThat(fs.encoding()).isEqualTo(Charset.forName("Cp1124")); + assertThat(fs.sourceCharset()).isEqualTo(Charset.forName("Cp1124")); + + // This test fails when default Java encoding is "IBM AIX Ukraine". Sorry for that. + assertThat(fs.isDefaultJvmEncoding()).isFalse(); + } + + @Test + public void default_predicate_scan_only_changed() throws IOException { + when(mode.scanAllFiles()).thenReturn(false); + + DefaultModuleFileSystem fs = new DefaultModuleFileSystem(moduleInputFileCache, + new Project("foo"), settings, fileIndexer, initializer, componentIndexer, mode); + + File baseDir = temp.newFile(); + InputFile mainInput = new DefaultInputFile("foo", "Main.java").setModuleBaseDir(baseDir.toPath()).setType(InputFile.Type.MAIN); + InputFile testInput = new DefaultInputFile("foo", "Test.java").setModuleBaseDir(baseDir.toPath()).setType(InputFile.Type.TEST); + InputFile mainSameInput = new DefaultInputFile("foo", "MainSame.java").setModuleBaseDir(baseDir.toPath()) + .setType(InputFile.Type.TEST).setStatus(Status.SAME); + when(moduleInputFileCache.inputFiles()).thenReturn(Lists.newArrayList(mainInput, testInput, mainSameInput)); + + fs.index(); + Iterable<InputFile> inputFiles = fs.inputFiles(fs.predicates().all()); + assertThat(inputFiles).containsOnly(mainInput, testInput); + + Iterable<InputFile> allInputFiles = fs.inputFiles(); + assertThat(allInputFiles).containsOnly(mainInput, mainSameInput, testInput); + } + + @Test + public void test_dirs() throws IOException { + File basedir = temp.newFolder("base"); + File buildDir = temp.newFolder("build"); + File workingDir = temp.newFolder("work"); + File additionalFile = temp.newFile("Main.java"); + File additionalTest = temp.newFile("Test.java"); + when(initializer.baseDir()).thenReturn(basedir); + when(initializer.buildDir()).thenReturn(buildDir); + when(initializer.workingDir()).thenReturn(workingDir); + when(initializer.binaryDirs()).thenReturn(Arrays.asList(new File(basedir, "target/classes"))); + File javaSrc = new File(basedir, "src/main/java"); + javaSrc.mkdirs(); + File groovySrc = new File(basedir, "src/main/groovy"); + groovySrc.mkdirs(); + when(initializer.sources()).thenReturn(Arrays.asList(javaSrc, groovySrc, additionalFile)); + File javaTest = new File(basedir, "src/test/java"); + javaTest.mkdirs(); + when(initializer.tests()).thenReturn(Arrays.asList(javaTest, additionalTest)); + + DefaultModuleFileSystem fs = new DefaultModuleFileSystem(moduleInputFileCache, + new Project("foo"), settings, fileIndexer, initializer, componentIndexer, mode); + + assertThat(fs.baseDir().getCanonicalPath()).isEqualTo(basedir.getCanonicalPath()); + assertThat(fs.workDir().getCanonicalPath()).isEqualTo(workingDir.getCanonicalPath()); + assertThat(fs.buildDir().getCanonicalPath()).isEqualTo(buildDir.getCanonicalPath()); + assertThat(fs.sourceDirs()).hasSize(2); + assertThat(fs.testDirs()).hasSize(1); + assertThat(fs.binaryDirs()).hasSize(1); + } + + @Test + public void should_search_input_files() throws Exception { + DefaultModuleFileSystem fs = new DefaultModuleFileSystem(moduleInputFileCache, + new Project("foo"), settings, fileIndexer, initializer, componentIndexer, mode); + + File baseDir = temp.newFile(); + InputFile mainInput = new DefaultInputFile("foo", "Main.java").setModuleBaseDir(baseDir.toPath()).setType(InputFile.Type.MAIN); + InputFile testInput = new DefaultInputFile("foo", "Test.java").setModuleBaseDir(baseDir.toPath()).setType(InputFile.Type.TEST); + when(moduleInputFileCache.inputFiles()).thenReturn(Lists.newArrayList(mainInput, testInput)); + + fs.index(); + Iterable<InputFile> inputFiles = fs.inputFiles(fs.predicates().hasType(InputFile.Type.MAIN)); + assertThat(inputFiles).containsOnly(mainInput); + + Iterable<File> files = fs.files(fs.predicates().hasType(InputFile.Type.MAIN)); + assertThat(files).containsOnly(new File(baseDir, "Main.java")); + } + + @Test + public void should_index() { + DefaultModuleFileSystem fs = new DefaultModuleFileSystem(moduleInputFileCache, + new Project("foo"), settings, fileIndexer, initializer, componentIndexer, mode); + + verifyZeroInteractions(fileIndexer); + + fs.index(); + verify(fileIndexer).index(fs); + verify(componentIndexer).execute(fs); + } + +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/filesystem/DeprecatedFileFiltersTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/filesystem/DeprecatedFileFiltersTest.java new file mode 100644 index 00000000000..e1905a12a9e --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/filesystem/DeprecatedFileFiltersTest.java @@ -0,0 +1,77 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.scan.filesystem; + +import org.apache.commons.io.FilenameUtils; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.mockito.ArgumentCaptor; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.scan.filesystem.FileSystemFilter; +import org.sonar.api.scan.filesystem.FileType; + +import java.io.File; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class DeprecatedFileFiltersTest { + + @Rule + public TemporaryFolder temp = new TemporaryFolder(); + + FileSystemFilter filter = mock(FileSystemFilter.class); + + @Test + public void no_filters() { + DeprecatedFileFilters filters = new DeprecatedFileFilters(); + + InputFile inputFile = new DefaultInputFile("foo", "src/main/java/Foo.java"); + assertThat(filters.accept(inputFile)).isTrue(); + } + + @Test + public void at_least_one_filter() throws Exception { + DeprecatedFileFilters filters = new DeprecatedFileFilters(new FileSystemFilter[] {filter}); + + File basedir = temp.newFolder(); + File file = new File(basedir, "src/main/java/Foo.java"); + InputFile inputFile = new DefaultInputFile("foo", "src/main/java/Foo.java") + .setModuleBaseDir(basedir.toPath()) + .setType(InputFile.Type.MAIN); + when(filter.accept(eq(file), any(DeprecatedFileFilters.DeprecatedContext.class))).thenReturn(false); + + assertThat(filters.accept(inputFile)).isFalse(); + + ArgumentCaptor<DeprecatedFileFilters.DeprecatedContext> argument = ArgumentCaptor.forClass(DeprecatedFileFilters.DeprecatedContext.class); + verify(filter).accept(eq(file), argument.capture()); + + DeprecatedFileFilters.DeprecatedContext context = argument.getValue(); + assertThat(context.canonicalPath()).isEqualTo(FilenameUtils.separatorsToUnix(file.getAbsolutePath())); + assertThat(context.relativePath()).isEqualTo("src/main/java/Foo.java"); + assertThat(context.type()).isEqualTo(FileType.MAIN); + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/filesystem/ExclusionFiltersTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/filesystem/ExclusionFiltersTest.java new file mode 100644 index 00000000000..c7b4e6eaa5e --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/filesystem/ExclusionFiltersTest.java @@ -0,0 +1,134 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.scan.filesystem; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.sonar.api.CoreProperties; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.config.Settings; +import org.sonar.api.scan.filesystem.FileExclusions; + +import java.io.File; +import java.io.IOException; + +import static org.assertj.core.api.Assertions.assertThat; + +public class ExclusionFiltersTest { + + @Rule + public TemporaryFolder temp = new TemporaryFolder(); + + @Test + public void no_inclusions_nor_exclusions() throws IOException { + ExclusionFilters filter = new ExclusionFilters(new FileExclusions(new Settings())); + filter.prepare(); + + java.io.File file = temp.newFile(); + DefaultInputFile inputFile = new DefaultInputFile("foo", "src/main/java/com/mycompany/FooDao.java").setModuleBaseDir(temp.newFolder().toPath()); + assertThat(filter.accept(inputFile, InputFile.Type.MAIN)).isTrue(); + assertThat(filter.accept(inputFile, InputFile.Type.TEST)).isTrue(); + } + + @Test + public void match_inclusion() throws IOException { + Settings settings = new Settings(); + settings.setProperty(CoreProperties.PROJECT_INCLUSIONS_PROPERTY, "**/*Dao.java"); + ExclusionFilters filter = new ExclusionFilters(new FileExclusions(settings)); + filter.prepare(); + + java.io.File file = temp.newFile(); + DefaultInputFile inputFile = new DefaultInputFile("foo", "src/main/java/com/mycompany/FooDao.java").setModuleBaseDir(temp.newFolder().toPath()); + assertThat(filter.accept(inputFile, InputFile.Type.MAIN)).isTrue(); + + inputFile = new DefaultInputFile("foo", "src/main/java/com/mycompany/Foo.java").setModuleBaseDir(temp.newFolder().toPath()); + assertThat(filter.accept(inputFile, InputFile.Type.MAIN)).isFalse(); + } + + @Test + public void match_at_least_one_inclusion() throws IOException { + Settings settings = new Settings(); + settings.setProperty(CoreProperties.PROJECT_INCLUSIONS_PROPERTY, "**/*Dao.java,**/*Dto.java"); + ExclusionFilters filter = new ExclusionFilters(new FileExclusions(settings)); + + filter.prepare(); + + java.io.File file = temp.newFile(); + + DefaultInputFile inputFile = new DefaultInputFile("foo", "src/main/java/com/mycompany/Foo.java").setModuleBaseDir(temp.newFolder().toPath()); + assertThat(filter.accept(inputFile, InputFile.Type.MAIN)).isFalse(); + + inputFile = new DefaultInputFile("foo", "src/main/java/com/mycompany/FooDto.java").setModuleBaseDir(temp.newFolder().toPath()); + assertThat(filter.accept(inputFile, InputFile.Type.MAIN)).isTrue(); + } + + @Test + public void match_exclusions() throws IOException { + Settings settings = new Settings(); + settings.setProperty(CoreProperties.PROJECT_INCLUSIONS_PROPERTY, "src/main/java/**/*"); + settings.setProperty(CoreProperties.PROJECT_TEST_INCLUSIONS_PROPERTY, "src/test/java/**/*"); + settings.setProperty(CoreProperties.PROJECT_EXCLUSIONS_PROPERTY, "**/*Dao.java"); + ExclusionFilters filter = new ExclusionFilters(new FileExclusions(settings)); + + filter.prepare(); + + java.io.File file = temp.newFile(); + DefaultInputFile inputFile = new DefaultInputFile("foo", "src/main/java/com/mycompany/FooDao.java").setModuleBaseDir(temp.newFolder().toPath()); + assertThat(filter.accept(inputFile, InputFile.Type.MAIN)).isFalse(); + + inputFile = new DefaultInputFile("foo", "src/main/java/com/mycompany/Foo.java").setModuleBaseDir(temp.newFolder().toPath()); + assertThat(filter.accept(inputFile, InputFile.Type.MAIN)).isTrue(); + + // source exclusions do not apply to tests + inputFile = new DefaultInputFile("foo", "src/test/java/com/mycompany/FooDao.java").setModuleBaseDir(temp.newFolder().toPath()); + assertThat(filter.accept(inputFile, InputFile.Type.TEST)).isTrue(); + } + + @Test + public void match_exclusion_by_absolute_path() throws IOException { + File baseDir = temp.newFile(); + File excludedFile = new File(baseDir, "src/main/java/org/bar/Bar.java"); + + Settings settings = new Settings(); + settings.setProperty(CoreProperties.PROJECT_INCLUSIONS_PROPERTY, "src/main/java/**/*"); + settings.setProperty(CoreProperties.PROJECT_EXCLUSIONS_PROPERTY, "file:" + excludedFile.getCanonicalPath()); + ExclusionFilters filter = new ExclusionFilters(new FileExclusions(settings)); + + filter.prepare(); + + DefaultInputFile inputFile = new DefaultInputFile("foo", "src/main/java/org/bar/Foo.java").setModuleBaseDir(baseDir.toPath()); + assertThat(filter.accept(inputFile, InputFile.Type.MAIN)).isTrue(); + + inputFile = new DefaultInputFile("foo", "src/main/java/org/bar/Bar.java").setModuleBaseDir(baseDir.toPath()); + assertThat(filter.accept(inputFile, InputFile.Type.MAIN)).isFalse(); + } + + @Test + public void trim_pattern() { + Settings settings = new Settings(); + settings.setProperty(CoreProperties.PROJECT_EXCLUSIONS_PROPERTY, " **/*Dao.java "); + ExclusionFilters filter = new ExclusionFilters(new FileExclusions(settings)); + + assertThat(filter.prepareMainExclusions()[0].toString()).isEqualTo("**/*Dao.java"); + } + +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/filesystem/InputFileBuilderFactoryTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/filesystem/InputFileBuilderFactoryTest.java new file mode 100644 index 00000000000..b9c627e1ea8 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/filesystem/InputFileBuilderFactoryTest.java @@ -0,0 +1,49 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.scan.filesystem; + +import org.junit.Test; +import org.mockito.Mockito; +import org.sonar.api.batch.bootstrap.ProjectDefinition; +import org.sonar.api.batch.fs.internal.FileMetadata; +import org.sonar.api.config.Settings; +import org.sonar.api.scan.filesystem.PathResolver; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + +public class InputFileBuilderFactoryTest { + @Test + public void create_builder() { + PathResolver pathResolver = new PathResolver(); + LanguageDetectionFactory langDetectionFactory = mock(LanguageDetectionFactory.class, Mockito.RETURNS_MOCKS); + StatusDetectionFactory statusDetectionFactory = mock(StatusDetectionFactory.class, Mockito.RETURNS_MOCKS); + DefaultModuleFileSystem fs = mock(DefaultModuleFileSystem.class); + + InputFileBuilderFactory factory = new InputFileBuilderFactory(ProjectDefinition.create().setKey("struts"), pathResolver, langDetectionFactory, + statusDetectionFactory, new Settings(), new FileMetadata()); + InputFileBuilder builder = factory.create(fs); + + assertThat(builder.langDetection()).isNotNull(); + assertThat(builder.statusDetection()).isNotNull(); + assertThat(builder.pathResolver()).isSameAs(pathResolver); + assertThat(builder.fs()).isSameAs(fs); + assertThat(builder.moduleKey()).isEqualTo("struts"); + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/filesystem/InputFileBuilderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/filesystem/InputFileBuilderTest.java new file mode 100644 index 00000000000..eaec22ca37a --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/filesystem/InputFileBuilderTest.java @@ -0,0 +1,118 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.scan.filesystem; + +import org.apache.commons.io.FileUtils; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.fs.internal.FileMetadata; +import org.sonar.api.config.Settings; +import org.sonar.api.scan.filesystem.PathResolver; +import org.sonar.api.utils.PathUtils; + +import java.io.File; +import java.nio.charset.StandardCharsets; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class InputFileBuilderTest { + + @Rule + public TemporaryFolder temp = new TemporaryFolder(); + + LanguageDetection langDetection = mock(LanguageDetection.class); + StatusDetection statusDetection = mock(StatusDetection.class); + DefaultModuleFileSystem fs = mock(DefaultModuleFileSystem.class); + + @Test + public void complete_input_file() throws Exception { + // file system + File basedir = temp.newFolder(); + File srcFile = new File(basedir, "src/main/java/foo/Bar.java"); + FileUtils.touch(srcFile); + FileUtils.write(srcFile, "single line"); + when(fs.baseDir()).thenReturn(basedir); + when(fs.encoding()).thenReturn(StandardCharsets.UTF_8); + + // lang + when(langDetection.language(any(InputFile.class))).thenReturn("java"); + + // status + when(statusDetection.status("foo", "src/main/java/foo/Bar.java", "6c1d64c0b3555892fe7273e954f6fb5a")) + .thenReturn(InputFile.Status.ADDED); + + InputFileBuilder builder = new InputFileBuilder("struts", new PathResolver(), + langDetection, statusDetection, fs, new Settings(), new FileMetadata()); + DefaultInputFile inputFile = builder.create(srcFile); + builder.completeAndComputeMetadata(inputFile, InputFile.Type.MAIN); + + assertThat(inputFile.type()).isEqualTo(InputFile.Type.MAIN); + assertThat(inputFile.file()).isEqualTo(srcFile.getAbsoluteFile()); + assertThat(inputFile.absolutePath()).isEqualTo(PathUtils.sanitize(srcFile.getAbsolutePath())); + assertThat(inputFile.language()).isEqualTo("java"); + assertThat(inputFile.key()).isEqualTo("struts:src/main/java/foo/Bar.java"); + assertThat(inputFile.relativePath()).isEqualTo("src/main/java/foo/Bar.java"); + assertThat(inputFile.lines()).isEqualTo(1); + } + + @Test + public void return_null_if_file_outside_basedir() throws Exception { + // file system + File basedir = temp.newFolder(); + File otherDir = temp.newFolder(); + File srcFile = new File(otherDir, "src/main/java/foo/Bar.java"); + FileUtils.touch(srcFile); + when(fs.baseDir()).thenReturn(basedir); + + InputFileBuilder builder = new InputFileBuilder("struts", new PathResolver(), + langDetection, statusDetection, fs, new Settings(), new FileMetadata()); + DefaultInputFile inputFile = builder.create(srcFile); + + assertThat(inputFile).isNull(); + } + + @Test + public void return_null_if_language_not_detected() throws Exception { + // file system + File basedir = temp.newFolder(); + File srcFile = new File(basedir, "src/main/java/foo/Bar.java"); + FileUtils.touch(srcFile); + FileUtils.write(srcFile, "single line"); + when(fs.baseDir()).thenReturn(basedir); + when(fs.encoding()).thenReturn(StandardCharsets.UTF_8); + + // lang + when(langDetection.language(any(InputFile.class))).thenReturn(null); + + InputFileBuilder builder = new InputFileBuilder("struts", new PathResolver(), + langDetection, statusDetection, fs, new Settings(), new FileMetadata()); + DefaultInputFile inputFile = builder.create(srcFile); + inputFile = builder.completeAndComputeMetadata(inputFile, InputFile.Type.MAIN); + + assertThat(inputFile).isNull(); + } + +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/filesystem/InputPathCacheTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/filesystem/InputPathCacheTest.java new file mode 100644 index 00000000000..23269eef7d3 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/filesystem/InputPathCacheTest.java @@ -0,0 +1,82 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.scan.filesystem; + +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.sonar.api.batch.fs.InputFile.Status; +import org.sonar.api.batch.fs.InputFile.Type; +import org.sonar.api.batch.fs.InputPath; +import org.sonar.api.batch.fs.internal.DefaultInputFile; + +import java.nio.charset.StandardCharsets; + +import static org.assertj.core.api.Assertions.assertThat; + +public class InputPathCacheTest { + + @Rule + public TemporaryFolder temp = new TemporaryFolder(); + + @Before + public void start() { + } + + @After + public void stop() { + } + + @Test + public void should_add_input_file() throws Exception { + InputPathCache cache = new InputPathCache(); + DefaultInputFile fooFile = new DefaultInputFile("foo", "src/main/java/Foo.java").setModuleBaseDir(temp.newFolder().toPath()); + cache.put("struts", fooFile); + cache.put("struts-core", new DefaultInputFile("foo", "src/main/java/Bar.java") + .setLanguage("bla") + .setType(Type.MAIN) + .setStatus(Status.ADDED) + .setLines(2) + .setCharset(StandardCharsets.UTF_8) + .setModuleBaseDir(temp.newFolder().toPath())); + + DefaultInputFile loadedFile = (DefaultInputFile) cache.getFile("struts-core", "src/main/java/Bar.java"); + assertThat(loadedFile.relativePath()).isEqualTo("src/main/java/Bar.java"); + assertThat(loadedFile.charset()).isEqualTo(StandardCharsets.UTF_8); + + assertThat(cache.filesByModule("struts")).hasSize(1); + assertThat(cache.filesByModule("struts-core")).hasSize(1); + assertThat(cache.allFiles()).hasSize(2); + for (InputPath inputPath : cache.allFiles()) { + assertThat(inputPath.relativePath()).startsWith("src/main/java/"); + } + + cache.remove("struts", fooFile); + assertThat(cache.allFiles()).hasSize(1); + + cache.removeModule("struts"); + assertThat(cache.filesByModule("struts")).hasSize(0); + assertThat(cache.filesByModule("struts-core")).hasSize(1); + assertThat(cache.allFiles()).hasSize(1); + } + +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/filesystem/LanguageDetectionFactoryTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/filesystem/LanguageDetectionFactoryTest.java new file mode 100644 index 00000000000..b30bb73036c --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/filesystem/LanguageDetectionFactoryTest.java @@ -0,0 +1,41 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.scan.filesystem; + +import org.junit.Test; +import org.sonar.api.config.Settings; +import org.sonar.api.resources.Java; +import org.sonar.api.resources.Languages; +import org.sonar.batch.repository.language.DefaultLanguagesRepository; +import org.sonar.batch.repository.language.LanguagesRepository; + +import static org.assertj.core.api.Assertions.assertThat; + +public class LanguageDetectionFactoryTest { + @Test + public void testCreate() throws Exception { + LanguagesRepository languages = new DefaultLanguagesRepository(new Languages(Java.INSTANCE)); + LanguageDetectionFactory factory = new LanguageDetectionFactory(new Settings(), languages); + LanguageDetection languageDetection = factory.create(); + assertThat(languageDetection).isNotNull(); + assertThat(languageDetection.patternsByLanguage()).hasSize(1); + assertThat(languageDetection.patternsByLanguage().containsKey("java")).isTrue(); + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/filesystem/LanguageDetectionTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/filesystem/LanguageDetectionTest.java new file mode 100644 index 00000000000..01a6a8aef3c --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/filesystem/LanguageDetectionTest.java @@ -0,0 +1,213 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.scan.filesystem; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.rules.TemporaryFolder; +import org.sonar.api.CoreProperties; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.config.Settings; +import org.sonar.api.resources.Language; +import org.sonar.api.resources.Languages; +import org.sonar.api.utils.MessageException; +import org.sonar.batch.repository.language.DefaultLanguagesRepository; +import org.sonar.batch.repository.language.LanguagesRepository; + +import java.io.File; +import java.io.IOException; + +import static junit.framework.Assert.fail; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.spy; + +public class LanguageDetectionTest { + + @Rule + public TemporaryFolder temp = new TemporaryFolder(); + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Test + public void test_sanitizeExtension() throws Exception { + assertThat(LanguageDetection.sanitizeExtension(".cbl")).isEqualTo("cbl"); + assertThat(LanguageDetection.sanitizeExtension(".CBL")).isEqualTo("cbl"); + assertThat(LanguageDetection.sanitizeExtension("CBL")).isEqualTo("cbl"); + assertThat(LanguageDetection.sanitizeExtension("cbl")).isEqualTo("cbl"); + } + + @Test + public void search_by_file_extension() throws Exception { + LanguagesRepository languages = new DefaultLanguagesRepository(new Languages(new MockLanguage("java", "java", "jav"), new MockLanguage("cobol", "cbl", "cob"))); + LanguageDetection detection = new LanguageDetection(new Settings(), languages); + + assertThat(detection.language(newInputFile("Foo.java"))).isEqualTo("java"); + assertThat(detection.language(newInputFile("src/Foo.java"))).isEqualTo("java"); + assertThat(detection.language(newInputFile("Foo.JAVA"))).isEqualTo("java"); + assertThat(detection.language(newInputFile("Foo.jav"))).isEqualTo("java"); + assertThat(detection.language(newInputFile("Foo.Jav"))).isEqualTo("java"); + + assertThat(detection.language(newInputFile("abc.cbl"))).isEqualTo("cobol"); + assertThat(detection.language(newInputFile("abc.CBL"))).isEqualTo("cobol"); + + assertThat(detection.language(newInputFile("abc.php"))).isNull(); + assertThat(detection.language(newInputFile("abc"))).isNull(); + } + + @Test + public void should_not_fail_if_no_language() throws Exception { + LanguageDetection detection = spy(new LanguageDetection(new Settings(), new DefaultLanguagesRepository(new Languages()))); + assertThat(detection.language(newInputFile("Foo.java"))).isNull(); + } + + @Test + public void plugin_can_declare_a_file_extension_twice_for_case_sensitivity() throws Exception { + LanguagesRepository languages = new DefaultLanguagesRepository(new Languages(new MockLanguage("abap", "abap", "ABAP"))); + + LanguageDetection detection = new LanguageDetection(new Settings(), languages); + assertThat(detection.language(newInputFile("abc.abap"))).isEqualTo("abap"); + } + + @Test + public void language_with_no_extension() throws Exception { + // abap does not declare any file extensions. + // When analyzing an ABAP project, then all source files must be parsed. + LanguagesRepository languages = new DefaultLanguagesRepository(new Languages(new MockLanguage("java", "java"), new MockLanguage("abap"))); + + // No side-effect on non-ABAP projects + LanguageDetection detection = new LanguageDetection(new Settings(), languages); + assertThat(detection.language(newInputFile("abc"))).isNull(); + assertThat(detection.language(newInputFile("abc.abap"))).isNull(); + assertThat(detection.language(newInputFile("abc.java"))).isEqualTo("java"); + + Settings settings = new Settings(); + settings.setProperty(CoreProperties.PROJECT_LANGUAGE_PROPERTY, "abap"); + detection = new LanguageDetection(settings, languages); + assertThat(detection.language(newInputFile("abc"))).isEqualTo("abap"); + assertThat(detection.language(newInputFile("abc.txt"))).isEqualTo("abap"); + assertThat(detection.language(newInputFile("abc.java"))).isEqualTo("abap"); + } + + @Test + public void force_language_using_deprecated_property() throws Exception { + LanguagesRepository languages = new DefaultLanguagesRepository(new Languages(new MockLanguage("java", "java"), new MockLanguage("php", "php"))); + + Settings settings = new Settings(); + settings.setProperty(CoreProperties.PROJECT_LANGUAGE_PROPERTY, "java"); + LanguageDetection detection = new LanguageDetection(settings, languages); + assertThat(detection.language(newInputFile("abc"))).isNull(); + assertThat(detection.language(newInputFile("abc.php"))).isNull(); + assertThat(detection.language(newInputFile("abc.java"))).isEqualTo("java"); + assertThat(detection.language(newInputFile("src/abc.java"))).isEqualTo("java"); + } + + @Test + public void fail_if_invalid_language() { + thrown.expect(MessageException.class); + thrown.expectMessage("No language is installed with key 'unknown'. Please update property 'sonar.language'"); + + LanguagesRepository languages = new DefaultLanguagesRepository(new Languages(new MockLanguage("java", "java"), new MockLanguage("php", "php"))); + Settings settings = new Settings(); + settings.setProperty(CoreProperties.PROJECT_LANGUAGE_PROPERTY, "unknown"); + new LanguageDetection(settings, languages); + } + + @Test + public void fail_if_conflicting_language_suffix() throws Exception { + LanguagesRepository languages = new DefaultLanguagesRepository(new Languages(new MockLanguage("xml", "xhtml"), new MockLanguage("web", "xhtml"))); + LanguageDetection detection = new LanguageDetection(new Settings(), languages); + try { + detection.language(newInputFile("abc.xhtml")); + fail(); + } catch (MessageException e) { + assertThat(e.getMessage()) + .contains("Language of file 'abc.xhtml' can not be decided as the file matches patterns of both ") + .contains("sonar.lang.patterns.web : **/*.xhtml") + .contains("sonar.lang.patterns.xml : **/*.xhtml"); + } + } + + @Test + public void solve_conflict_using_filepattern() throws Exception { + LanguagesRepository languages = new DefaultLanguagesRepository(new Languages(new MockLanguage("xml", "xhtml"), new MockLanguage("web", "xhtml"))); + + Settings settings = new Settings(); + settings.setProperty("sonar.lang.patterns.xml", "xml/**"); + settings.setProperty("sonar.lang.patterns.web", "web/**"); + LanguageDetection detection = new LanguageDetection(settings, languages); + assertThat(detection.language(newInputFile("xml/abc.xhtml"))).isEqualTo("xml"); + assertThat(detection.language(newInputFile("web/abc.xhtml"))).isEqualTo("web"); + } + + @Test + public void fail_if_conflicting_filepattern() throws Exception { + LanguagesRepository languages = new DefaultLanguagesRepository(new Languages(new MockLanguage("abap", "abap"), new MockLanguage("cobol", "cobol"))); + Settings settings = new Settings(); + settings.setProperty("sonar.lang.patterns.abap", "*.abap,*.txt"); + settings.setProperty("sonar.lang.patterns.cobol", "*.cobol,*.txt"); + + LanguageDetection detection = new LanguageDetection(settings, languages); + + assertThat(detection.language(newInputFile("abc.abap"))).isEqualTo("abap"); + assertThat(detection.language(newInputFile("abc.cobol"))).isEqualTo("cobol"); + try { + detection.language(newInputFile("abc.txt")); + fail(); + } catch (MessageException e) { + assertThat(e.getMessage()) + .contains("Language of file 'abc.txt' can not be decided as the file matches patterns of both ") + .contains("sonar.lang.patterns.abap : *.abap,*.txt") + .contains("sonar.lang.patterns.cobol : *.cobol,*.txt"); + } + } + + private InputFile newInputFile(String path) throws IOException { + File basedir = temp.newFolder(); + return new DefaultInputFile("foo", path).setModuleBaseDir(basedir.toPath()); + } + + static class MockLanguage implements Language { + private final String key; + private final String[] extensions; + + MockLanguage(String key, String... extensions) { + this.key = key; + this.extensions = extensions; + } + + @Override + public String getKey() { + return key; + } + + @Override + public String getName() { + return key; + } + + @Override + public String[] getFileSuffixes() { + return extensions; + } + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/filesystem/ModuleFileSystemInitializerTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/filesystem/ModuleFileSystemInitializerTest.java new file mode 100644 index 00000000000..08dcf9197e5 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/filesystem/ModuleFileSystemInitializerTest.java @@ -0,0 +1,92 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.scan.filesystem; + +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.FilenameUtils; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.sonar.api.batch.bootstrap.ProjectDefinition; +import org.sonar.api.scan.filesystem.PathResolver; +import org.sonar.api.utils.TempFolder; + +import java.io.File; +import java.io.IOException; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + +public class ModuleFileSystemInitializerTest { + + @Rule + public TemporaryFolder temp = new TemporaryFolder(); + + PathResolver pathResolver = new PathResolver(); + + @Test + public void test_default_directories() throws Exception { + File baseDir = temp.newFolder("base"); + File workDir = temp.newFolder("work"); + ProjectDefinition module = ProjectDefinition.create().setBaseDir(baseDir).setWorkDir(workDir); + + ModuleFileSystemInitializer initializer = new ModuleFileSystemInitializer(module, mock(TempFolder.class), pathResolver); + + assertThat(initializer.baseDir().getCanonicalPath()).isEqualTo(baseDir.getCanonicalPath()); + assertThat(initializer.workingDir().getCanonicalPath()).isEqualTo(workDir.getCanonicalPath()); + assertThat(initializer.sources()).isEmpty(); + assertThat(initializer.tests()).isEmpty(); + } + + @Test + public void should_init_directories() throws IOException { + File baseDir = temp.newFolder("base"); + File buildDir = temp.newFolder("build"); + File sourceDir = new File(baseDir, "src/main/java"); + FileUtils.forceMkdir(sourceDir); + File testDir = new File(baseDir, "src/test/java"); + FileUtils.forceMkdir(testDir); + File binaryDir = new File(baseDir, "target/classes"); + FileUtils.forceMkdir(binaryDir); + + ProjectDefinition project = ProjectDefinition.create() + .setBaseDir(baseDir) + .setBuildDir(buildDir) + .addSourceDirs("src/main/java", "src/main/unknown") + .addTestDirs("src/test/java", "src/test/unknown") + .addBinaryDir("target/classes"); + + ModuleFileSystemInitializer initializer = new ModuleFileSystemInitializer(project, mock(TempFolder.class), pathResolver); + + assertThat(initializer.baseDir().getCanonicalPath()).isEqualTo(baseDir.getCanonicalPath()); + assertThat(initializer.buildDir().getCanonicalPath()).isEqualTo(buildDir.getCanonicalPath()); + assertThat(initializer.sources()).hasSize(1); + assertThat(path(initializer.sources().get(0))).endsWith("src/main/java"); + assertThat(initializer.tests()).hasSize(1); + assertThat(path(initializer.tests().get(0))).endsWith("src/test/java"); + assertThat(initializer.binaryDirs()).hasSize(1); + assertThat(path(initializer.binaryDirs().get(0))).endsWith("target/classes"); + } + + private String path(File f) throws IOException { + return FilenameUtils.separatorsToUnix(f.getCanonicalPath()); + } + +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/filesystem/StatusDetectionFactoryTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/filesystem/StatusDetectionFactoryTest.java new file mode 100644 index 00000000000..b741f1eb344 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/filesystem/StatusDetectionFactoryTest.java @@ -0,0 +1,35 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.scan.filesystem; + +import org.sonar.batch.repository.ProjectRepositories; + +import org.junit.Test; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + +public class StatusDetectionFactoryTest { + @Test + public void testCreate() throws Exception { + StatusDetectionFactory factory = new StatusDetectionFactory(mock(ProjectRepositories.class)); + StatusDetection detection = factory.create(); + assertThat(detection).isNotNull(); + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/filesystem/StatusDetectionTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/filesystem/StatusDetectionTest.java new file mode 100644 index 00000000000..0601fa26b3b --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/filesystem/StatusDetectionTest.java @@ -0,0 +1,52 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.scan.filesystem; + +import org.sonar.batch.repository.FileData; + +import com.google.common.collect.HashBasedTable; +import com.google.common.collect.ImmutableTable; +import com.google.common.collect.Table; +import org.junit.Test; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.batch.repository.ProjectRepositories; +import static org.assertj.core.api.Assertions.assertThat; + +public class StatusDetectionTest { + @Test + public void detect_status() { + Table<String, String, String> t = ImmutableTable.of(); + ProjectRepositories ref = new ProjectRepositories(t, createTable(), null); + StatusDetection statusDetection = new StatusDetection(ref); + + assertThat(statusDetection.status("foo", "src/Foo.java", "ABCDE")).isEqualTo(InputFile.Status.SAME); + assertThat(statusDetection.status("foo", "src/Foo.java", "XXXXX")).isEqualTo(InputFile.Status.CHANGED); + assertThat(statusDetection.status("foo", "src/Other.java", "QWERT")).isEqualTo(InputFile.Status.ADDED); + } + + private static Table<String, String, FileData> createTable() { + Table<String, String, FileData> t = HashBasedTable.create(); + + t.put("foo", "src/Foo.java", new FileData("ABCDE", "12345789")); + t.put("foo", "src/Bar.java", new FileData("FGHIJ", "123456789")); + + return t; + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/measure/MeasureCacheTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/measure/MeasureCacheTest.java new file mode 100644 index 00000000000..61f21529547 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/measure/MeasureCacheTest.java @@ -0,0 +1,231 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.scan.measure; + +import java.util.Date; +import java.util.Iterator; +import org.apache.commons.lang.builder.EqualsBuilder; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.sonar.api.batch.measure.MetricFinder; +import org.sonar.api.measures.CoreMetrics; +import org.sonar.api.measures.Measure; +import org.sonar.api.measures.Metric.Level; +import org.sonar.api.resources.Directory; +import org.sonar.api.resources.File; +import org.sonar.api.resources.Project; +import org.sonar.api.resources.Resource; +import org.sonar.batch.index.AbstractCachesTest; +import org.sonar.batch.index.Cache.Entry; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class MeasureCacheTest extends AbstractCachesTest { + @Rule + public ExpectedException thrown = ExpectedException.none(); + + private MetricFinder metricFinder; + + private MeasureCache measureCache; + + @Before + public void start() { + super.start(); + metricFinder = mock(MetricFinder.class); + when(metricFinder.findByKey(CoreMetrics.NCLOC_KEY)).thenReturn(CoreMetrics.NCLOC); + measureCache = new MeasureCache(caches, metricFinder); + } + + @Test + public void should_add_measure() { + Project p = new Project("struts"); + + assertThat(measureCache.entries()).hasSize(0); + assertThat(measureCache.byResource(p)).hasSize(0); + + Measure m = new Measure(CoreMetrics.NCLOC, 1.0); + measureCache.put(p, m); + + assertThat(measureCache.contains(p, m)).isTrue(); + assertThat(measureCache.entries()).hasSize(1); + Iterator<Entry<Measure>> iterator = measureCache.entries().iterator(); + iterator.hasNext(); + Entry<Measure> next = iterator.next(); + assertThat(next.value()).isEqualTo(m); + assertThat(next.key()[0]).isEqualTo("struts"); + + assertThat(measureCache.byResource(p)).hasSize(1); + assertThat(measureCache.byResource(p).iterator().next()).isEqualTo(m); + } + + @Test + public void should_add_measure_with_big_data() { + Project p = new Project("struts"); + + assertThat(measureCache.entries()).hasSize(0); + + assertThat(measureCache.byResource(p)).hasSize(0); + + Measure m = new Measure(CoreMetrics.NCLOC, 1.0).setDate(new Date()); + m.setAlertText("foooooooooooooooooooooooooooooooooooo"); + StringBuilder data = new StringBuilder(); + for (int i = 0; i < 1_048_575; i++) { + data.append("a"); + } + + m.setData(data.toString()); + + measureCache.put(p, m); + + assertThat(measureCache.contains(p, m)).isTrue(); + assertThat(measureCache.entries()).hasSize(1); + Iterator<Entry<Measure>> iterator = measureCache.entries().iterator(); + iterator.hasNext(); + Entry<Measure> next = iterator.next(); + assertThat(next.value()).isEqualTo(m); + assertThat(next.key()[0]).isEqualTo("struts"); + + assertThat(measureCache.byResource(p)).hasSize(1); + assertThat(measureCache.byResource(p).iterator().next()).isEqualTo(m); + } + + /** + * This test fails with stock PersisitIt. + */ + @Test + public void should_add_measure_with_too_big_data_for_persistit_pre_patch() { + Project p = new Project("struts"); + + assertThat(measureCache.entries()).hasSize(0); + + assertThat(measureCache.byResource(p)).hasSize(0); + + Measure m = new Measure(CoreMetrics.NCLOC, 1.0).setDate(new Date()); + StringBuilder data = new StringBuilder(); + for (int i = 0; i < 500000; i++) { + data.append("some data"); + } + m.setData(data.toString()); + + measureCache.put(p, m); + + assertThat(measureCache.contains(p, m)).isTrue(); + assertThat(measureCache.entries()).hasSize(1); + Iterator<Entry<Measure>> iterator = measureCache.entries().iterator(); + iterator.hasNext(); + Entry<Measure> next = iterator.next(); + assertThat(next.value()).isEqualTo(m); + assertThat(next.key()[0]).isEqualTo("struts"); + + assertThat(measureCache.byResource(p)).hasSize(1); + assertThat(measureCache.byResource(p).iterator().next()).isEqualTo(m); + + } + + @Test + public void should_add_measure_with_too_big_data_for_persistit() { + Project p = new Project("struts"); + + assertThat(measureCache.entries()).hasSize(0); + + assertThat(measureCache.byResource(p)).hasSize(0); + + Measure m = new Measure(CoreMetrics.NCLOC, 1.0).setDate(new Date()); + StringBuilder data = new StringBuilder(64 * 1024 * 1024 + 1); + // Limit is 64Mo + for (int i = 0; i < (64 * 1024 * 1024 + 1); i++) { + data.append('a'); + } + m.setData(data.toString()); + + thrown.expect(IllegalStateException.class); + thrown.expectMessage("Fail to put element in the cache measures"); + + measureCache.put(p, m); + } + + @Test + public void should_get_measures() { + Project p = new Project("struts"); + Resource dir = Directory.create("foo/bar").setEffectiveKey("struts:foo/bar"); + Resource file1 = Directory.create("foo/bar/File1.txt").setEffectiveKey("struts:foo/bar/File1.txt"); + Resource file2 = Directory.create("foo/bar/File2.txt").setEffectiveKey("struts:foo/bar/File2.txt"); + + assertThat(measureCache.entries()).hasSize(0); + + assertThat(measureCache.byResource(p)).hasSize(0); + assertThat(measureCache.byResource(dir)).hasSize(0); + + Measure mFile1 = new Measure(CoreMetrics.NCLOC, 1.0); + measureCache.put(file1, mFile1); + Measure mFile2 = new Measure(CoreMetrics.NCLOC, 3.0); + measureCache.put(file2, mFile2); + + assertThat(measureCache.entries()).hasSize(2); + assertThat(measureCache.byResource(p)).hasSize(0); + assertThat(measureCache.byResource(dir)).hasSize(0); + + Measure mDir = new Measure(CoreMetrics.NCLOC, 4.0); + measureCache.put(dir, mDir); + + assertThat(measureCache.entries()).hasSize(3); + assertThat(measureCache.byResource(p)).hasSize(0); + assertThat(measureCache.byResource(dir)).hasSize(1); + assertThat(measureCache.byResource(dir).iterator().next()).isEqualTo(mDir); + + Measure mProj = new Measure(CoreMetrics.NCLOC, 4.0); + measureCache.put(p, mProj); + + assertThat(measureCache.entries()).hasSize(4); + assertThat(measureCache.byResource(p)).hasSize(1); + assertThat(measureCache.byResource(p).iterator().next()).isEqualTo(mProj); + assertThat(measureCache.byResource(dir)).hasSize(1); + assertThat(measureCache.byResource(dir).iterator().next()).isEqualTo(mDir); + } + + @Test + public void test_measure_coder() throws Exception { + Resource file1 = File.create("foo/bar/File1.txt").setEffectiveKey("struts:foo/bar/File1.txt"); + + Measure measure = new Measure(CoreMetrics.NCLOC, 3.14); + measure.setData("data"); + measure.setAlertStatus(Level.ERROR); + measure.setAlertText("alert"); + measure.setDate(new Date()); + measure.setDescription("description"); + measure.setPersistenceMode(null); + measure.setPersonId(3); + measure.setUrl("http://foo"); + measure.setVariation1(11.0); + measure.setVariation2(12.0); + measure.setVariation3(13.0); + measure.setVariation4(14.0); + measure.setVariation5(15.0); + measureCache.put(file1, measure); + + Measure savedMeasure = measureCache.byResource(file1).iterator().next(); + assertThat(EqualsBuilder.reflectionEquals(measure, savedMeasure)).isTrue(); + + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/report/ConsoleReportTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/report/ConsoleReportTest.java new file mode 100644 index 00000000000..1a49bb94731 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/report/ConsoleReportTest.java @@ -0,0 +1,145 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.scan.report; + +import javax.annotation.Nullable; + +import org.sonar.batch.issue.tracking.TrackedIssue; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.config.Settings; +import org.sonar.api.rule.Severity; +import org.sonar.api.utils.log.LogTester; +import org.sonar.batch.issue.IssueCache; +import org.sonar.batch.scan.filesystem.InputPathCache; + +import java.util.Arrays; +import java.util.Collections; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class ConsoleReportTest { + + @Rule + public LogTester logTester = new LogTester(); + + private Settings settings; + private IssueCache issueCache; + private InputPathCache inputPathCache; + private ConsoleReport report; + + @Before + public void prepare() { + settings = new Settings(); + issueCache = mock(IssueCache.class); + inputPathCache = mock(InputPathCache.class); + report = new ConsoleReport(settings, issueCache, inputPathCache); + } + + @Test + public void dontExecuteByDefault() { + report.execute(); + for (String log : logTester.logs()) { + assertThat(log).doesNotContain(ConsoleReport.HEADER); + } + } + + @Test + public void testNoFile() { + settings.setProperty(ConsoleReport.CONSOLE_REPORT_ENABLED_KEY, "true"); + when(inputPathCache.allFiles()).thenReturn(Collections.<InputFile>emptyList()); + when(issueCache.all()).thenReturn(Collections.<TrackedIssue>emptyList()); + report.execute(); + assertThat(getReportLog()).isEqualTo( + "\n\n------------- Issues Report -------------\n\n" + + " No file analyzed\n" + + "\n-------------------------------------------\n\n"); + } + + @Test + public void testNoNewIssue() { + settings.setProperty(ConsoleReport.CONSOLE_REPORT_ENABLED_KEY, "true"); + when(inputPathCache.allFiles()).thenReturn(Arrays.<InputFile>asList(new DefaultInputFile("foo", "src/Foo.php"))); + when(issueCache.all()).thenReturn(Arrays.asList(createIssue(false, null))); + report.execute(); + assertThat(getReportLog()).isEqualTo( + "\n\n------------- Issues Report -------------\n\n" + + " No new issue\n" + + "\n-------------------------------------------\n\n"); + } + + @Test + public void testOneNewIssue() { + settings.setProperty(ConsoleReport.CONSOLE_REPORT_ENABLED_KEY, "true"); + when(inputPathCache.allFiles()).thenReturn(Arrays.<InputFile>asList(new DefaultInputFile("foo", "src/Foo.php"))); + when(issueCache.all()).thenReturn(Arrays.asList(createIssue(true, Severity.BLOCKER))); + report.execute(); + assertThat(getReportLog()).isEqualTo( + "\n\n------------- Issues Report -------------\n\n" + + " +1 issue\n\n" + + " +1 blocker\n" + + "\n-------------------------------------------\n\n"); + } + + @Test + public void testOneNewIssuePerSeverity() { + settings.setProperty(ConsoleReport.CONSOLE_REPORT_ENABLED_KEY, "true"); + when(inputPathCache.allFiles()).thenReturn(Arrays.<InputFile>asList(new DefaultInputFile("foo", "src/Foo.php"))); + when(issueCache.all()).thenReturn(Arrays.asList( + createIssue(true, Severity.BLOCKER), + createIssue(true, Severity.CRITICAL), + createIssue(true, Severity.MAJOR), + createIssue(true, Severity.MINOR), + createIssue(true, Severity.INFO))); + report.execute(); + assertThat(getReportLog()).isEqualTo( + "\n\n------------- Issues Report -------------\n\n" + + " +5 issues\n\n" + + " +1 blocker\n" + + " +1 critical\n" + + " +1 major\n" + + " +1 minor\n" + + " +1 info\n" + + "\n-------------------------------------------\n\n"); + } + + private String getReportLog() { + for (String log : logTester.logs()) { + if (log.contains(ConsoleReport.HEADER)) { + return log; + } + } + throw new IllegalStateException("No console report"); + } + + private TrackedIssue createIssue(boolean isNew, @Nullable String severity) { + TrackedIssue issue = new TrackedIssue(); + issue.setNew(isNew); + issue.setSeverity(severity); + + return issue; + } + +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/report/JSONReportTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/report/JSONReportTest.java new file mode 100644 index 00000000000..da798a2dbf1 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/report/JSONReportTest.java @@ -0,0 +1,174 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.scan.report; + +import com.google.common.collect.Lists; +import java.io.File; +import java.io.IOException; +import java.io.StringWriter; +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Collections; +import java.util.TimeZone; +import org.apache.commons.io.IOUtils; +import org.junit.Before; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.sonar.api.batch.fs.InputDir; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.fs.internal.DefaultFileSystem; +import org.sonar.api.batch.fs.internal.DefaultInputDir; +import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.rule.Rules; +import org.sonar.api.batch.rule.internal.RulesBuilder; +import org.sonar.api.config.Settings; +import org.sonar.api.issue.Issue; +import org.sonar.api.platform.Server; +import org.sonar.api.resources.Project; +import org.sonar.api.resources.Resource; +import org.sonar.api.rule.RuleKey; +import org.sonar.batch.issue.IssueCache; +import org.sonar.batch.issue.tracking.TrackedIssue; +import org.sonar.batch.repository.user.UserRepositoryLoader; +import org.sonar.batch.scan.filesystem.InputPathCache; +import org.sonar.scanner.protocol.input.ScannerInput; + +import static net.javacrumbs.jsonunit.assertj.JsonAssert.assertThatJson; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.when; + +public class JSONReportTest { + + private SimpleDateFormat SIMPLE_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd"); + + @org.junit.Rule + public TemporaryFolder temp = new TemporaryFolder(); + + JSONReport jsonReport; + Resource resource = mock(Resource.class); + DefaultFileSystem fs; + Server server = mock(Server.class); + Rules rules = mock(Rules.class); + Settings settings = new Settings(); + IssueCache issueCache = mock(IssueCache.class); + private UserRepositoryLoader userRepository; + + @Before + public void before() throws Exception { + fs = new DefaultFileSystem(temp.newFolder().toPath()); + SIMPLE_DATE_FORMAT.setTimeZone(TimeZone.getTimeZone("GMT+02:00")); + when(resource.getEffectiveKey()).thenReturn("Action.java"); + when(server.getVersion()).thenReturn("3.6"); + userRepository = mock(UserRepositoryLoader.class); + DefaultInputDir inputDir = new DefaultInputDir("struts", "src/main/java/org/apache/struts"); + DefaultInputFile inputFile = new DefaultInputFile("struts", "src/main/java/org/apache/struts/Action.java"); + inputFile.setStatus(InputFile.Status.CHANGED); + InputPathCache fileCache = mock(InputPathCache.class); + when(fileCache.allFiles()).thenReturn(Arrays.<InputFile>asList(inputFile)); + when(fileCache.allDirs()).thenReturn(Arrays.<InputDir>asList(inputDir)); + Project rootModule = new Project("struts"); + Project moduleA = new Project("struts-core"); + moduleA.setParent(rootModule).setPath("core"); + Project moduleB = new Project("struts-ui"); + moduleB.setParent(rootModule).setPath("ui"); + + RulesBuilder builder = new RulesBuilder(); + builder.add(RuleKey.of("squid", "AvoidCycles")).setName("Avoid Cycles"); + rules = builder.build(); + jsonReport = new JSONReport(settings, fs, server, rules, issueCache, rootModule, fileCache, userRepository); + } + + @Test + public void should_write_json() throws Exception { + TrackedIssue issue = new TrackedIssue(); + issue.setKey("200"); + issue.setComponentKey("struts:src/main/java/org/apache/struts/Action.java"); + issue.setRuleKey(RuleKey.of("squid", "AvoidCycles")); + issue.setMessage("There are 2 cycles"); + issue.setSeverity("MINOR"); + issue.setStatus(Issue.STATUS_OPEN); + issue.setResolution(null); + issue.setStartLine(1); + issue.setEndLine(2); + issue.setStartLineOffset(3); + issue.setEndLineOffset(4); + issue.setGap(3.14); + issue.setReporter("julien"); + issue.setAssignee("simon"); + issue.setCreationDate(SIMPLE_DATE_FORMAT.parse("2013-04-24")); + issue.setNew(false); + when(issueCache.all()).thenReturn(Lists.newArrayList(issue)); + ScannerInput.User user1 = ScannerInput.User.newBuilder().setLogin("julien").setName("Julien").build(); + ScannerInput.User user2 = ScannerInput.User.newBuilder().setLogin("simon").setName("Simon").build(); + when(userRepository.load("julien")).thenReturn(user1); + when(userRepository.load("simon")).thenReturn(user2); + + StringWriter writer = new StringWriter(); + jsonReport.writeJson(writer); + + assertThatJson(writer.toString()).isEqualTo(IOUtils.toString(this.getClass().getResource(this.getClass().getSimpleName() + "/report.json"))); + } + + @Test + public void should_exclude_resolved_issues() throws Exception { + RuleKey ruleKey = RuleKey.of("squid", "AvoidCycles"); + TrackedIssue issue = new TrackedIssue(); + issue.setKey("200"); + issue.setComponentKey("struts:src/main/java/org/apache/struts/Action.java"); + issue.setRuleKey(ruleKey); + issue.setStatus(Issue.STATUS_CLOSED); + issue.setResolution(Issue.RESOLUTION_FIXED); + issue.setCreationDate(SIMPLE_DATE_FORMAT.parse("2013-04-24")); + issue.setNew(false); + when(issueCache.all()).thenReturn(Lists.newArrayList(issue)); + + StringWriter writer = new StringWriter(); + jsonReport.writeJson(writer); + + assertThatJson(writer.toString()).isEqualTo(IOUtils.toString(this.getClass().getResource(this.getClass().getSimpleName() + "/report-without-resolved-issues.json"))); + } + + @Test + public void should_not_export_by_default() throws IOException { + File workDir = temp.newFolder("sonar"); + fs.setWorkDir(workDir); + + jsonReport.execute(); + + verifyZeroInteractions(issueCache); + } + + @Test + public void should_export_issues_to_file() throws IOException { + File workDir = temp.newFolder("sonar"); + fs.setWorkDir(workDir); + + when(issueCache.all()).thenReturn(Collections.<TrackedIssue>emptyList()); + + settings.setProperty("sonar.report.export.path", "output.json"); + + jsonReport.execute(); + + assertThat(new File(workDir, "output.json")).exists(); + } + +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/report/RuleNameProviderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/report/RuleNameProviderTest.java new file mode 100644 index 00000000000..3c3af0e689c --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/report/RuleNameProviderTest.java @@ -0,0 +1,67 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.scan.report; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.assertj.core.api.Assertions.assertThat; + +import static org.mockito.Matchers.any; + +import org.sonar.api.rule.RuleKey; +import org.sonar.api.batch.rule.Rule; +import org.junit.Test; +import org.junit.Before; +import org.sonar.api.batch.rule.Rules; + +public class RuleNameProviderTest { + RuleNameProvider provider; + Rules rules; + Rule rule; + RuleKey ruleKey; + + @Before + public void setUp() { + ruleKey = mock(RuleKey.class); + rule = mock(Rule.class); + rules = mock(Rules.class); + provider = new RuleNameProvider(rules); + + when(ruleKey.rule()).thenReturn("ruleKey"); + when(ruleKey.repository()).thenReturn("repoKey"); + + when(rule.name()).thenReturn("name"); + when(rule.key()).thenReturn(ruleKey); + + when(rules.find(any(RuleKey.class))).thenReturn(rule); + } + + @Test + public void testNameForHTML() { + assertThat(provider.nameForHTML(rule)).isEqualTo(rule.name()); + assertThat(provider.nameForHTML(ruleKey)).isEqualTo(rule.name()); + } + + @Test + public void testNameForJS() { + assertThat(provider.nameForJS("repoKey:ruleKey")).isEqualTo(rule.name()); + } + +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/scm/DefaultBlameOutputTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/scm/DefaultBlameOutputTest.java new file mode 100644 index 00000000000..743f9175ba4 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/scm/DefaultBlameOutputTest.java @@ -0,0 +1,94 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.scm; + +import java.util.Arrays; +import java.util.Date; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.sonar.api.batch.fs.InputComponent; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.scm.BlameLine; +import org.sonar.batch.index.BatchComponent; +import org.sonar.batch.index.BatchComponentCache; + +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class DefaultBlameOutputTest { + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + private BatchComponentCache componentCache; + + @Before + public void prepare() { + componentCache = mock(BatchComponentCache.class); + BatchComponent component = mock(BatchComponent.class); + when(component.batchId()).thenReturn(1); + when(componentCache.get(any(InputComponent.class))).thenReturn(component); + } + + @Test + public void shouldNotFailIfNotSameNumberOfLines() { + InputFile file = new DefaultInputFile("foo", "src/main/java/Foo.java").setLines(10); + + new DefaultBlameOutput(null, null, Arrays.asList(file)).blameResult(file, Arrays.asList(new BlameLine().revision("1").author("guy"))); + } + + @Test + public void shouldFailIfNotExpectedFile() { + InputFile file = new DefaultInputFile("foo", "src/main/java/Foo.java").setLines(1); + + thrown.expect(IllegalArgumentException.class); + thrown.expectMessage("It was not expected to blame file src/main/java/Foo.java"); + + new DefaultBlameOutput(null, null, Arrays.<InputFile>asList(new DefaultInputFile("foo", "src/main/java/Foo2.java"))) + .blameResult(file, Arrays.asList(new BlameLine().revision("1").author("guy"))); + } + + @Test + public void shouldFailIfNullDate() { + InputFile file = new DefaultInputFile("foo", "src/main/java/Foo.java").setLines(1); + + thrown.expect(IllegalArgumentException.class); + thrown.expectMessage("Blame date is null for file src/main/java/Foo.java at line 1"); + + new DefaultBlameOutput(null, componentCache, Arrays.<InputFile>asList(file)) + .blameResult(file, Arrays.asList(new BlameLine().revision("1").author("guy"))); + } + + @Test + public void shouldFailIfNullRevision() { + InputFile file = new DefaultInputFile("foo", "src/main/java/Foo.java").setLines(1); + + thrown.expect(IllegalArgumentException.class); + thrown.expectMessage("Blame revision is blank for file src/main/java/Foo.java at line 1"); + + new DefaultBlameOutput(null, componentCache, Arrays.<InputFile>asList(file)) + .blameResult(file, Arrays.asList(new BlameLine().date(new Date()).author("guy"))); + } + +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/sensor/DefaultSensorContextTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/sensor/DefaultSensorContextTest.java new file mode 100644 index 00000000000..ba40858840d --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/sensor/DefaultSensorContextTest.java @@ -0,0 +1,79 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.sensor; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.rules.TemporaryFolder; +import org.sonar.api.batch.AnalysisMode; +import org.sonar.api.batch.fs.InputModule; +import org.sonar.api.batch.fs.internal.DefaultFileSystem; +import org.sonar.api.batch.measure.MetricFinder; +import org.sonar.api.batch.rule.ActiveRules; +import org.sonar.api.batch.rule.internal.ActiveRulesBuilder; +import org.sonar.api.batch.sensor.internal.SensorStorage; +import org.sonar.api.config.Settings; +import org.sonar.api.measures.CoreMetrics; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class DefaultSensorContextTest { + + @Rule + public TemporaryFolder temp = new TemporaryFolder(); + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + private ActiveRules activeRules; + private DefaultFileSystem fs; + private DefaultSensorContext adaptor; + private Settings settings; + private SensorStorage sensorStorage; + private AnalysisMode analysisMode; + + @Before + public void prepare() throws Exception { + activeRules = new ActiveRulesBuilder().build(); + fs = new DefaultFileSystem(temp.newFolder().toPath()); + MetricFinder metricFinder = mock(MetricFinder.class); + when(metricFinder.findByKey(CoreMetrics.NCLOC_KEY)).thenReturn(CoreMetrics.NCLOC); + when(metricFinder.findByKey(CoreMetrics.FUNCTION_COMPLEXITY_DISTRIBUTION_KEY)).thenReturn(CoreMetrics.FUNCTION_COMPLEXITY_DISTRIBUTION); + settings = new Settings(); + sensorStorage = mock(SensorStorage.class); + analysisMode = mock(AnalysisMode.class); + adaptor = new DefaultSensorContext(mock(InputModule.class), settings, fs, activeRules, analysisMode, sensorStorage); + } + + @Test + public void shouldProvideComponents() { + assertThat(adaptor.activeRules()).isEqualTo(activeRules); + assertThat(adaptor.fileSystem()).isEqualTo(fs); + assertThat(adaptor.settings()).isEqualTo(settings); + + assertThat(adaptor.newIssue()).isNotNull(); + assertThat(adaptor.newMeasure()).isNotNull(); + } + +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/sensor/DefaultSensorStorageTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/sensor/DefaultSensorStorageTest.java new file mode 100644 index 00000000000..caa16b11a19 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/sensor/DefaultSensorStorageTest.java @@ -0,0 +1,140 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.sensor; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.rules.TemporaryFolder; +import org.mockito.ArgumentCaptor; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.fs.internal.DefaultFileSystem; +import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.fs.internal.DefaultInputModule; +import org.sonar.api.batch.measure.MetricFinder; +import org.sonar.api.batch.rule.ActiveRules; +import org.sonar.api.batch.rule.internal.ActiveRulesBuilder; +import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure; +import org.sonar.api.config.Settings; +import org.sonar.api.measures.CoreMetrics; +import org.sonar.api.measures.Measure; +import org.sonar.api.resources.File; +import org.sonar.api.resources.Project; +import org.sonar.api.resources.Resource; +import org.sonar.batch.cpd.index.SonarCpdBlockIndex; +import org.sonar.batch.index.BatchComponentCache; +import org.sonar.batch.issue.ModuleIssues; +import org.sonar.batch.report.ReportPublisher; +import org.sonar.batch.scan.measure.MeasureCache; +import org.sonar.batch.sensor.coverage.CoverageExclusions; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class DefaultSensorStorageTest { + + @Rule + public TemporaryFolder temp = new TemporaryFolder(); + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + private ActiveRules activeRules; + private DefaultFileSystem fs; + private DefaultSensorStorage sensorStorage; + private Settings settings; + private ModuleIssues moduleIssues; + private Project project; + private MeasureCache measureCache; + + private BatchComponentCache resourceCache; + + @Before + public void prepare() throws Exception { + activeRules = new ActiveRulesBuilder().build(); + fs = new DefaultFileSystem(temp.newFolder().toPath()); + MetricFinder metricFinder = mock(MetricFinder.class); + when(metricFinder.findByKey(CoreMetrics.NCLOC_KEY)).thenReturn(CoreMetrics.NCLOC); + when(metricFinder.findByKey(CoreMetrics.FUNCTION_COMPLEXITY_DISTRIBUTION_KEY)).thenReturn(CoreMetrics.FUNCTION_COMPLEXITY_DISTRIBUTION); + settings = new Settings(); + moduleIssues = mock(ModuleIssues.class); + project = new Project("myProject"); + measureCache = mock(MeasureCache.class); + CoverageExclusions coverageExclusions = mock(CoverageExclusions.class); + when(coverageExclusions.accept(any(Resource.class), any(Measure.class))).thenReturn(true); + resourceCache = new BatchComponentCache(); + sensorStorage = new DefaultSensorStorage(metricFinder, + moduleIssues, settings, fs, activeRules, coverageExclusions, resourceCache, mock(ReportPublisher.class), measureCache, mock(SonarCpdBlockIndex.class)); + } + + @Test + public void shouldFailIfUnknowMetric() { + InputFile file = new DefaultInputFile("foo", "src/Foo.php"); + + thrown.expect(IllegalStateException.class); + thrown.expectMessage("Unknow metric with key: lines"); + + sensorStorage.store(new DefaultMeasure() + .on(file) + .forMetric(CoreMetrics.LINES) + .withValue(10)); + } + + @Test + public void shouldSaveFileMeasureToSensorContext() { + InputFile file = new DefaultInputFile("foo", "src/Foo.php"); + + ArgumentCaptor<org.sonar.api.measures.Measure> argumentCaptor = ArgumentCaptor.forClass(org.sonar.api.measures.Measure.class); + Resource sonarFile = File.create("src/Foo.php").setEffectiveKey("foo:src/Foo.php"); + resourceCache.add(sonarFile, null).setInputComponent(file); + when(measureCache.put(eq(sonarFile), argumentCaptor.capture())).thenReturn(null); + sensorStorage.store(new DefaultMeasure() + .on(file) + .forMetric(CoreMetrics.NCLOC) + .withValue(10)); + + org.sonar.api.measures.Measure m = argumentCaptor.getValue(); + assertThat(m.getValue()).isEqualTo(10.0); + assertThat(m.getMetric()).isEqualTo(CoreMetrics.NCLOC); + } + + @Test + public void shouldSaveProjectMeasureToSensorContext() { + DefaultInputModule module = new DefaultInputModule(project.getEffectiveKey()); + resourceCache.add(project, null).setInputComponent(module); + + ArgumentCaptor<org.sonar.api.measures.Measure> argumentCaptor = ArgumentCaptor.forClass(org.sonar.api.measures.Measure.class); + when(measureCache.put(eq(project), argumentCaptor.capture())).thenReturn(null); + + sensorStorage.store(new DefaultMeasure() + .on(module) + .forMetric(CoreMetrics.NCLOC) + .withValue(10)); + + org.sonar.api.measures.Measure m = argumentCaptor.getValue(); + assertThat(m.getValue()).isEqualTo(10.0); + assertThat(m.getMetric()).isEqualTo(CoreMetrics.NCLOC); + } + +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/sensor/SensorOptimizerTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/sensor/SensorOptimizerTest.java new file mode 100644 index 00000000000..0fac9a72139 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/sensor/SensorOptimizerTest.java @@ -0,0 +1,136 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.sensor; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.rules.TemporaryFolder; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.fs.internal.DefaultFileSystem; +import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.rule.ActiveRules; +import org.sonar.api.batch.rule.internal.ActiveRulesBuilder; +import org.sonar.api.batch.sensor.internal.DefaultSensorDescriptor; +import org.sonar.api.config.Settings; +import org.sonar.api.rule.RuleKey; + +import static org.assertj.core.api.Assertions.assertThat; + +public class SensorOptimizerTest { + + @Rule + public TemporaryFolder temp = new TemporaryFolder(); + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + private DefaultFileSystem fs; + private SensorOptimizer optimizer; + private Settings settings; + + @Before + public void prepare() throws Exception { + fs = new DefaultFileSystem(temp.newFolder().toPath()); + settings = new Settings(); + optimizer = new SensorOptimizer(fs, new ActiveRulesBuilder().build(), settings); + } + + @Test + public void should_run_analyzer_with_no_metadata() { + DefaultSensorDescriptor descriptor = new DefaultSensorDescriptor(); + + assertThat(optimizer.shouldExecute(descriptor)).isTrue(); + } + + @Test + public void should_optimize_on_language() { + DefaultSensorDescriptor descriptor = new DefaultSensorDescriptor() + .onlyOnLanguages("java", "php"); + assertThat(optimizer.shouldExecute(descriptor)).isFalse(); + + fs.add(new DefaultInputFile("foo", "src/Foo.java").setLanguage("java")); + assertThat(optimizer.shouldExecute(descriptor)).isTrue(); + } + + @Test + public void should_optimize_on_type() { + DefaultSensorDescriptor descriptor = new DefaultSensorDescriptor() + .onlyOnFileType(InputFile.Type.MAIN); + assertThat(optimizer.shouldExecute(descriptor)).isFalse(); + + fs.add(new DefaultInputFile("foo", "tests/FooTest.java").setType(InputFile.Type.TEST)); + assertThat(optimizer.shouldExecute(descriptor)).isFalse(); + + fs.add(new DefaultInputFile("foo", "src/Foo.java").setType(InputFile.Type.MAIN)); + assertThat(optimizer.shouldExecute(descriptor)).isTrue(); + } + + @Test + public void should_optimize_on_both_type_and_language() { + DefaultSensorDescriptor descriptor = new DefaultSensorDescriptor() + .onlyOnLanguages("java", "php") + .onlyOnFileType(InputFile.Type.MAIN); + assertThat(optimizer.shouldExecute(descriptor)).isFalse(); + + fs.add(new DefaultInputFile("foo", "tests/FooTest.java").setLanguage("java").setType(InputFile.Type.TEST)); + fs.add(new DefaultInputFile("foo", "src/Foo.cbl").setLanguage("cobol").setType(InputFile.Type.MAIN)); + assertThat(optimizer.shouldExecute(descriptor)).isFalse(); + + fs.add(new DefaultInputFile("foo", "src/Foo.java").setLanguage("java").setType(InputFile.Type.MAIN)); + assertThat(optimizer.shouldExecute(descriptor)).isTrue(); + } + + @Test + public void should_optimize_on_repository() { + DefaultSensorDescriptor descriptor = new DefaultSensorDescriptor() + .createIssuesForRuleRepositories("squid"); + assertThat(optimizer.shouldExecute(descriptor)).isFalse(); + + ActiveRules activeRules = new ActiveRulesBuilder() + .create(RuleKey.of("repo1", "foo")) + .activate() + .build(); + optimizer = new SensorOptimizer(fs, activeRules, settings); + + assertThat(optimizer.shouldExecute(descriptor)).isFalse(); + + activeRules = new ActiveRulesBuilder() + .create(RuleKey.of("repo1", "foo")) + .activate() + .create(RuleKey.of("squid", "rule")) + .activate() + .build(); + optimizer = new SensorOptimizer(fs, activeRules, settings); + assertThat(optimizer.shouldExecute(descriptor)).isTrue(); + } + + @Test + public void should_optimize_on_settings() { + DefaultSensorDescriptor descriptor = new DefaultSensorDescriptor() + .requireProperty("sonar.foo.reportPath"); + assertThat(optimizer.shouldExecute(descriptor)).isFalse(); + + settings.setProperty("sonar.foo.reportPath", "foo"); + assertThat(optimizer.shouldExecute(descriptor)).isTrue(); + } + +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/sensor/coverage/CoverageExclusionsTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/sensor/coverage/CoverageExclusionsTest.java new file mode 100644 index 00000000000..284226f9af0 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/sensor/coverage/CoverageExclusionsTest.java @@ -0,0 +1,143 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.sensor.coverage; + +import org.junit.rules.TemporaryFolder; + +import org.sonar.api.batch.fs.internal.DefaultFileSystem; +import org.sonar.api.batch.fs.internal.DefaultInputFile; +import com.google.common.collect.ImmutableMap; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.sonar.api.config.PropertyDefinitions; +import org.sonar.api.config.Settings; +import org.sonar.api.measures.CoreMetrics; +import org.sonar.api.measures.Measure; +import org.sonar.api.resources.File; +import org.sonar.api.resources.Resource; +import org.sonar.api.utils.KeyValueFormat; +import org.sonar.core.config.ExclusionProperties; + +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class CoverageExclusionsTest { + + @Rule + public ExpectedException exception = ExpectedException.none(); + + @Rule + public TemporaryFolder temp = new TemporaryFolder(); + + private Settings settings; + private DefaultFileSystem fs; + + private CoverageExclusions filter; + + @Before + public void createFilter() { + settings = new Settings(new PropertyDefinitions(ExclusionProperties.all())); + fs = new DefaultFileSystem(temp.getRoot()); + filter = new CoverageExclusions(settings, fs); + } + + @Test + public void shouldValidateStrictlyPositiveLine() { + DefaultInputFile file = new DefaultInputFile("module", "testfile"); + Measure measure = mock(Measure.class); + Map<Integer, Integer> map = ImmutableMap.of(0, 3); + + String data = KeyValueFormat.format(map); + when(measure.getMetric()).thenReturn(CoreMetrics.IT_CONDITIONS_BY_LINE); + when(measure.getData()).thenReturn(data); + + fs.add(file); + + exception.expect(IllegalStateException.class); + exception.expectMessage("must be > 0"); + filter.validate(measure, "testfile"); + } + + @Test + public void shouldValidateFileExists() { + DefaultInputFile file = new DefaultInputFile("module", "testfile"); + Measure measure = mock(Measure.class); + Map<Integer, Integer> map = ImmutableMap.of(0, 3); + + String data = KeyValueFormat.format(map); + when(measure.getMetric()).thenReturn(CoreMetrics.IT_CONDITIONS_BY_LINE); + when(measure.getData()).thenReturn(data); + + fs.add(file); + + exception.expect(IllegalStateException.class); + exception.expectMessage("resource is not indexed as a file"); + filter.validate(measure, "dummy"); + } + + @Test + public void shouldValidateMaxLine() { + DefaultInputFile file = new DefaultInputFile("module", "testfile"); + file.setLines(10); + Measure measure = mock(Measure.class); + Map<Integer, Integer> map = ImmutableMap.of(11, 3); + + String data = KeyValueFormat.format(map); + when(measure.getMetric()).thenReturn(CoreMetrics.COVERED_CONDITIONS_BY_LINE); + when(measure.getData()).thenReturn(data); + + exception.expect(IllegalStateException.class); + filter.validate(measure, file); + } + + @Test + public void shouldNotFilterNonCoverageMetrics() { + Measure otherMeasure = mock(Measure.class); + when(otherMeasure.getMetric()).thenReturn(CoreMetrics.LINES); + assertThat(filter.accept(mock(Resource.class), otherMeasure)).isTrue(); + } + + @Test + public void shouldFilterFileBasedOnPattern() { + Resource resource = File.create("src/org/polop/File.php", null, false); + Measure coverageMeasure = mock(Measure.class); + when(coverageMeasure.getMetric()).thenReturn(CoreMetrics.LINES_TO_COVER); + + settings.setProperty("sonar.coverage.exclusions", "src/org/polop/*"); + filter.initPatterns(); + assertThat(filter.accept(resource, coverageMeasure)).isFalse(); + } + + @Test + public void shouldNotFilterFileBasedOnPattern() { + Resource resource = File.create("src/org/polop/File.php", null, false); + Measure coverageMeasure = mock(Measure.class); + when(coverageMeasure.getMetric()).thenReturn(CoreMetrics.COVERAGE); + + settings.setProperty("sonar.coverage.exclusions", "src/org/other/*"); + filter.initPatterns(); + assertThat(filter.accept(resource, coverageMeasure)).isTrue(); + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/source/CodeColorizersTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/source/CodeColorizersTest.java new file mode 100644 index 00000000000..2a9530e1142 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/source/CodeColorizersTest.java @@ -0,0 +1,230 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.source; + +import com.google.common.collect.ImmutableList; +import java.io.File; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import org.apache.commons.io.FileUtils; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.fs.internal.FileMetadata; +import org.sonar.api.batch.sensor.highlighting.NewHighlighting; +import org.sonar.api.batch.sensor.highlighting.TypeOfText; +import org.sonar.api.batch.sensor.highlighting.internal.DefaultHighlighting; +import org.sonar.api.batch.sensor.internal.SensorStorage; +import org.sonar.api.web.CodeColorizerFormat; +import org.sonar.colorizer.CDocTokenizer; +import org.sonar.colorizer.CppDocTokenizer; +import org.sonar.colorizer.JavadocTokenizer; +import org.sonar.colorizer.KeywordsTokenizer; +import org.sonar.colorizer.MultilinesDocTokenizer; +import org.sonar.colorizer.RegexpTokenizer; +import org.sonar.colorizer.StringTokenizer; +import org.sonar.colorizer.Tokenizer; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.tuple; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +public class CodeColorizersTest { + + @Rule + public TemporaryFolder temp = new TemporaryFolder(); + + @Test + public void testConvertToHighlighting() throws Exception { + CodeColorizers codeColorizers = new CodeColorizers(Arrays.<CodeColorizerFormat>asList(new JavaScriptColorizerFormat(), new WebCodeColorizerFormat())); + File jsFile = new File(this.getClass().getResource("CodeColorizersTest/Person.js").toURI()); + NewHighlighting highlighting = mock(NewHighlighting.class); + + codeColorizers.toSyntaxHighlighting(jsFile, StandardCharsets.UTF_8, "js", highlighting); + + verifyForJs(highlighting); + } + + private void verifyForJs(NewHighlighting highlighting) { + verify(highlighting).highlight(0, 4, TypeOfText.CPP_DOC); + verify(highlighting).highlight(5, 11, TypeOfText.CPP_DOC); + verify(highlighting).highlight(12, 15, TypeOfText.CPP_DOC); + verify(highlighting).highlight(16, 19, TypeOfText.KEYWORD); + verify(highlighting).highlight(29, 37, TypeOfText.KEYWORD); + verify(highlighting).highlight(65, 69, TypeOfText.KEYWORD); + verify(highlighting).highlight(85, 93, TypeOfText.COMMENT); + verify(highlighting).highlight(98, 102, TypeOfText.KEYWORD); + verify(highlighting).highlight(112, 114, TypeOfText.STRING); + verify(highlighting).highlight(120, 124, TypeOfText.KEYWORD); + } + + @Test + public void testConvertToHighlightingIgnoreBOM() throws Exception { + CodeColorizers codeColorizers = new CodeColorizers(Arrays.<CodeColorizerFormat>asList(new JavaScriptColorizerFormat(), new WebCodeColorizerFormat())); + + File fileWithBom = temp.newFile(); + FileUtils.write(fileWithBom, "\uFEFF", "UTF-8"); + File jsFile = new File(this.getClass().getResource("CodeColorizersTest/Person.js").toURI()); + FileUtils.write(fileWithBom, FileUtils.readFileToString(jsFile), "UTF-8", true); + + NewHighlighting highlighting = mock(NewHighlighting.class); + codeColorizers.toSyntaxHighlighting(fileWithBom, StandardCharsets.UTF_8, "js", highlighting); + + verifyForJs(highlighting); + } + + @Test + public void shouldSupportJavaIfNotProvidedByJavaPluginForBackwardCompatibility() throws Exception { + CodeColorizers codeColorizers = new CodeColorizers(Arrays.<CodeColorizerFormat>asList()); + + File javaFile = new File(this.getClass().getResource("CodeColorizersTest/Person.java").toURI()); + + NewHighlighting highlighting = mock(NewHighlighting.class); + codeColorizers.toSyntaxHighlighting(javaFile, StandardCharsets.UTF_8, "java", highlighting); + + verify(highlighting).highlight(0, 4, TypeOfText.STRUCTURED_COMMENT); + verify(highlighting).highlight(5, 11, TypeOfText.STRUCTURED_COMMENT); + verify(highlighting).highlight(12, 15, TypeOfText.STRUCTURED_COMMENT); + verify(highlighting).highlight(16, 22, TypeOfText.KEYWORD); + verify(highlighting).highlight(23, 28, TypeOfText.KEYWORD); + verify(highlighting).highlight(43, 50, TypeOfText.KEYWORD); + verify(highlighting).highlight(51, 54, TypeOfText.KEYWORD); + verify(highlighting).highlight(67, 78, TypeOfText.ANNOTATION); + verify(highlighting).highlight(81, 87, TypeOfText.KEYWORD); + verify(highlighting).highlight(88, 92, TypeOfText.KEYWORD); + verify(highlighting).highlight(97, 100, TypeOfText.KEYWORD); + verify(highlighting).highlight(142, 146, TypeOfText.KEYWORD); + verify(highlighting).highlight(162, 170, TypeOfText.COMMENT); + } + + @Test + public void testConvertHtmlToHighlightingWithMacEoL() throws Exception { + CodeColorizers codeColorizers = new CodeColorizers(Arrays.<CodeColorizerFormat>asList(new JavaScriptColorizerFormat(), new WebCodeColorizerFormat())); + File htmlFile = new File(this.getClass().getResource("CodeColorizersTest/package.html").toURI()); + SensorStorage sensorStorage = mock(SensorStorage.class); + DefaultHighlighting highlighting = new DefaultHighlighting(sensorStorage); + highlighting.onFile(new DefaultInputFile("FOO", "package.html") + .initMetadata(new FileMetadata().readMetadata(htmlFile, StandardCharsets.UTF_8))); + + codeColorizers.toSyntaxHighlighting(htmlFile, StandardCharsets.UTF_8, "web", highlighting); + + assertThat(highlighting.getSyntaxHighlightingRuleSet()).extracting("range.start.line", "range.start.lineOffset", "range.end.line", "range.end.lineOffset", "textType") + .containsExactly( + tuple(1, 0, 1, 132, TypeOfText.STRUCTURED_COMMENT), + tuple(2, 0, 2, 6, TypeOfText.KEYWORD), + tuple(3, 0, 3, 3, TypeOfText.KEYWORD), + tuple(4, 0, 4, 3, TypeOfText.KEYWORD), + // SONARWEB-26 + tuple(5, 42, 12, 0, TypeOfText.STRING)); + } + + public static class JavaScriptColorizerFormat extends CodeColorizerFormat { + + public JavaScriptColorizerFormat() { + super("js"); + } + + @Override + public List<Tokenizer> getTokenizers() { + return ImmutableList.<Tokenizer>of( + new StringTokenizer("<span class=\"s\">", "</span>"), + new CDocTokenizer("<span class=\"cd\">", "</span>"), + new JavadocTokenizer("<span class=\"cppd\">", "</span>"), + new CppDocTokenizer("<span class=\"cppd\">", "</span>"), + new KeywordsTokenizer("<span class=\"k\">", "</span>", "null", + "true", + "false", + "break", + "case", + "catch", + "class", + "continue", + "debugger", + "default", + "delete", + "do", + "extends", + "else", + "finally", + "for", + "function", + "if", + "import", + "in", + "instanceof", + "new", + "return", + "super", + "switch", + "this", + "throw", + "try", + "typeof", + "var", + "void", + "while", + "with", + "yield", + "const", + "enum", + "export")); + } + + } + + public class WebCodeColorizerFormat extends CodeColorizerFormat { + + private final List<Tokenizer> tokenizers = new ArrayList<>(); + + public WebCodeColorizerFormat() { + super("web"); + String tagAfter = "</span>"; + + // == tags == + tokenizers.add(new RegexpTokenizer("<span class=\"k\">", tagAfter, "</?[:\\w]+>?")); + tokenizers.add(new RegexpTokenizer("<span class=\"k\">", tagAfter, ">")); + + // == doctype == + tokenizers.add(new RegexpTokenizer("<span class=\"j\">", tagAfter, "<!DOCTYPE.*>")); + + // == comments == + tokenizers.add(new MultilinesDocTokenizer("<!--", "-->", "<span class=\"j\">", tagAfter)); + tokenizers.add(new MultilinesDocTokenizer("<%--", "--%>", "<span class=\"j\">", tagAfter)); + + // == expressions == + tokenizers.add(new MultilinesDocTokenizer("<%@", "%>", "<span class=\"a\">", tagAfter)); + tokenizers.add(new MultilinesDocTokenizer("<%", "%>", "<span class=\"a\">", tagAfter)); + + // == tag properties == + tokenizers.add(new StringTokenizer("<span class=\"s\">", tagAfter)); + } + + @Override + public List<Tokenizer> getTokenizers() { + return tokenizers; + } + + } + +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/source/DefaultHighlightableTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/source/DefaultHighlightableTest.java new file mode 100644 index 00000000000..3a791783b1d --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/source/DefaultHighlightableTest.java @@ -0,0 +1,55 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.source; + +import java.io.StringReader; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.mockito.ArgumentCaptor; +import org.sonar.api.batch.AnalysisMode; +import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.fs.internal.FileMetadata; +import org.sonar.api.batch.sensor.highlighting.internal.DefaultHighlighting; +import org.sonar.api.batch.sensor.internal.SensorStorage; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +public class DefaultHighlightableTest { + + @Rule + public ExpectedException throwable = ExpectedException.none(); + + @Test + public void should_store_highlighting_rules() { + SensorStorage sensorStorage = mock(SensorStorage.class); + DefaultInputFile inputFile = new DefaultInputFile("foo", "src/Foo.php") + .initMetadata(new FileMetadata().readMetadata(new StringReader("azerty\nbla bla"))); + DefaultHighlightable highlightablePerspective = new DefaultHighlightable(inputFile, sensorStorage, mock(AnalysisMode.class)); + highlightablePerspective.newHighlighting().highlight(0, 6, "k").highlight(7, 10, "cppd").done(); + + ArgumentCaptor<DefaultHighlighting> argCaptor = ArgumentCaptor.forClass(DefaultHighlighting.class); + verify(sensorStorage).store(argCaptor.capture()); + assertThat(argCaptor.getValue().getSyntaxHighlightingRuleSet()).hasSize(2); + } + +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/source/DefaultSymbolTableTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/source/DefaultSymbolTableTest.java new file mode 100644 index 00000000000..86c7b7b316d --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/source/DefaultSymbolTableTest.java @@ -0,0 +1,100 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.source; + +import org.sonar.api.batch.fs.TextRange; +import com.google.common.base.Strings; + +import java.io.StringReader; +import java.util.Set; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.fs.internal.FileMetadata; +import org.sonar.api.source.Symbol; +import org.sonar.api.source.Symbolizable; +import static org.assertj.core.api.Assertions.assertThat; + +public class DefaultSymbolTableTest { + + @Rule + public ExpectedException throwable = ExpectedException.none(); + private DefaultInputFile inputFile; + + @Before + public void prepare() { + inputFile = new DefaultInputFile("foo", "src/Foo.php") + .initMetadata(new FileMetadata().readMetadata(new StringReader(Strings.repeat("azerty\n", 20)))); + } + + @Test + public void should_order_symbol_and_references() { + + Symbolizable.SymbolTableBuilder symbolTableBuilder = new DefaultSymbolTable.Builder(inputFile); + Symbol firstSymbol = symbolTableBuilder.newSymbol(10, 20); + symbolTableBuilder.newReference(firstSymbol, 32); + Symbol secondSymbol = symbolTableBuilder.newSymbol(84, 92); + symbolTableBuilder.newReference(secondSymbol, 124); + Symbol thirdSymbol = symbolTableBuilder.newSymbol(55, 62); + symbolTableBuilder.newReference(thirdSymbol, 70); + Symbolizable.SymbolTable symbolTable = symbolTableBuilder.build(); + + assertThat(symbolTable.symbols()).containsExactly(firstSymbol, secondSymbol, thirdSymbol); + } + + @Test + public void variable_length_references() { + Symbolizable.SymbolTableBuilder symbolTableBuilder = new DefaultSymbolTable.Builder(inputFile); + Symbol firstSymbol = symbolTableBuilder.newSymbol(10, 20); + symbolTableBuilder.newReference(firstSymbol, 32); + symbolTableBuilder.newReference(firstSymbol, 44, 47); + + DefaultSymbolTable symbolTable = (DefaultSymbolTable) symbolTableBuilder.build(); + + assertThat(symbolTable.symbols()).containsExactly(firstSymbol); + + Set<TextRange> references = symbolTable.getReferencesBySymbol().get(firstSymbol); + assertThat(references).containsExactly(range(32, 42), range(44, 47)); + } + + private TextRange range(int start, int end) { + return inputFile.newRange(start, end); + } + + @Test + public void should_reject_reference_conflicting_with_declaration() { + throwable.expect(UnsupportedOperationException.class); + + Symbolizable.SymbolTableBuilder symbolTableBuilder = new DefaultSymbolTable.Builder(inputFile); + Symbol symbol = symbolTableBuilder.newSymbol(10, 20); + symbolTableBuilder.newReference(symbol, 15); + } + + @Test + public void test_toString() throws Exception { + Symbolizable.SymbolTableBuilder symbolTableBuilder = new DefaultSymbolTable.Builder(inputFile); + Symbol symbol = symbolTableBuilder.newSymbol(10, 20); + + assertThat(symbol.toString()).isEqualTo("Symbol{range=Range[from [line=2, lineOffset=3] to [line=3, lineOffset=6]]}"); + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/source/DefaultSymbolizableTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/source/DefaultSymbolizableTest.java new file mode 100644 index 00000000000..f19a4b04099 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/source/DefaultSymbolizableTest.java @@ -0,0 +1,66 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.source; + +import com.google.common.base.Strings; +import java.io.StringReader; +import java.util.Map; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.sonar.api.batch.AnalysisMode; +import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.fs.internal.FileMetadata; +import org.sonar.api.source.Symbol; +import org.sonar.api.source.Symbolizable; +import org.sonar.batch.sensor.DefaultSensorStorage; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +public class DefaultSymbolizableTest { + + @Test + public void should_update_cache_when_done() { + + DefaultSensorStorage sensorStorage = mock(DefaultSensorStorage.class); + DefaultInputFile inputFile = new DefaultInputFile("foo", "src/Foo.php") + .initMetadata(new FileMetadata().readMetadata(new StringReader(Strings.repeat("azerty\n", 20)))); + + DefaultSymbolizable symbolPerspective = new DefaultSymbolizable(inputFile, sensorStorage, mock(AnalysisMode.class)); + Symbolizable.SymbolTableBuilder symbolTableBuilder = symbolPerspective.newSymbolTableBuilder(); + Symbol firstSymbol = symbolTableBuilder.newSymbol(4, 8); + symbolTableBuilder.newReference(firstSymbol, 12); + symbolTableBuilder.newReference(firstSymbol, 70); + Symbol otherSymbol = symbolTableBuilder.newSymbol(25, 33); + symbolTableBuilder.newReference(otherSymbol, 44); + symbolTableBuilder.newReference(otherSymbol, 60); + symbolTableBuilder.newReference(otherSymbol, 108); + Symbolizable.SymbolTable symbolTable = symbolTableBuilder.build(); + + symbolPerspective.setSymbolTable(symbolTable); + + ArgumentCaptor<Map> argCaptor = ArgumentCaptor.forClass(Map.class); + verify(sensorStorage).store(eq(inputFile), argCaptor.capture()); + // Map<Symbol, Set<TextRange>> + assertThat(argCaptor.getValue().keySet()).hasSize(2); + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/source/HighlightableBuilderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/source/HighlightableBuilderTest.java new file mode 100644 index 00000000000..07ca60048b3 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/source/HighlightableBuilderTest.java @@ -0,0 +1,58 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.source; + +import org.junit.Test; +import org.sonar.api.batch.AnalysisMode; +import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.fs.internal.DefaultInputModule; +import org.sonar.api.batch.sensor.internal.SensorStorage; +import org.sonar.api.resources.File; +import org.sonar.api.resources.Project; +import org.sonar.api.resources.Resource; +import org.sonar.api.source.Highlightable; +import org.sonar.batch.index.BatchComponent; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + +public class HighlightableBuilderTest { + + @Test + public void should_load_default_perspective() { + Resource file = File.create("foo.c").setEffectiveKey("myproject:path/to/foo.c"); + BatchComponent component = new BatchComponent(1, file, null).setInputComponent(new DefaultInputFile("foo", "foo.c")); + + HighlightableBuilder builder = new HighlightableBuilder(mock(SensorStorage.class), mock(AnalysisMode.class)); + Highlightable perspective = builder.loadPerspective(Highlightable.class, component); + + assertThat(perspective).isNotNull().isInstanceOf(DefaultHighlightable.class); + } + + @Test + public void project_should_not_be_highlightable() { + BatchComponent component = new BatchComponent(1, new Project("struts").setEffectiveKey("org.struts"), null).setInputComponent(new DefaultInputModule("struts")); + + HighlightableBuilder builder = new HighlightableBuilder(mock(SensorStorage.class), mock(AnalysisMode.class)); + Highlightable perspective = builder.loadPerspective(Highlightable.class, component); + + assertThat(perspective).isNull(); + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/source/SymbolizableBuilderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/source/SymbolizableBuilderTest.java new file mode 100644 index 00000000000..5cc7fe2d06b --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/source/SymbolizableBuilderTest.java @@ -0,0 +1,59 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.source; + +import org.junit.Test; +import org.sonar.api.batch.AnalysisMode; +import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.fs.internal.DefaultInputModule; +import org.sonar.api.component.Perspective; +import org.sonar.api.resources.File; +import org.sonar.api.resources.Project; +import org.sonar.api.resources.Resource; +import org.sonar.api.source.Symbolizable; +import org.sonar.batch.index.BatchComponent; +import org.sonar.batch.sensor.DefaultSensorStorage; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + +public class SymbolizableBuilderTest { + + @Test + public void should_load_perspective() { + Resource file = File.create("foo.c").setEffectiveKey("myproject:path/to/foo.c"); + BatchComponent component = new BatchComponent(1, file, null).setInputComponent(new DefaultInputFile("foo", "foo.c")); + + SymbolizableBuilder perspectiveBuilder = new SymbolizableBuilder(mock(DefaultSensorStorage.class), mock(AnalysisMode.class)); + Perspective perspective = perspectiveBuilder.loadPerspective(Symbolizable.class, component); + + assertThat(perspective).isInstanceOf(Symbolizable.class); + } + + @Test + public void project_should_not_be_highlightable() { + BatchComponent component = new BatchComponent(1, new Project("struts").setEffectiveKey("org.struts"), null).setInputComponent(new DefaultInputModule("struts")); + + SymbolizableBuilder builder = new SymbolizableBuilder(mock(DefaultSensorStorage.class), mock(AnalysisMode.class)); + Perspective perspective = builder.loadPerspective(Symbolizable.class, component); + + assertThat(perspective).isNull(); + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/task/ListTaskTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/task/ListTaskTest.java new file mode 100644 index 00000000000..0bf7ef0afc2 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/task/ListTaskTest.java @@ -0,0 +1,63 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.task; + +import java.util.Arrays; +import org.junit.Rule; +import org.junit.Test; +import org.sonar.api.task.Task; +import org.sonar.api.task.TaskDefinition; +import org.sonar.api.utils.log.LogTester; +import org.sonar.api.utils.log.LoggerLevel; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class ListTaskTest { + + @Rule + public LogTester logTester = new LogTester(); + + @Test + public void should_list_available_tasks() { + Tasks tasks = mock(Tasks.class); + when(tasks.definitions()).thenReturn(Arrays.asList( + TaskDefinition.builder().key("foo").description("Foo").taskClass(FooTask.class).build(), + TaskDefinition.builder().key("purge").description("Purge database").taskClass(FakePurgeTask.class).build())); + + ListTask task = new ListTask(tasks); + + task.execute(); + + assertThat(logTester.logs(LoggerLevel.INFO)).hasSize(1); + assertThat(logTester.logs(LoggerLevel.INFO).get(0)).contains("Available tasks:", " - foo: Foo", " - purge: Purge database"); + } + + private static class FakePurgeTask implements Task { + public void execute() { + } + } + + private static class FooTask implements Task { + public void execute() { + } + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/task/TasksTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/task/TasksTest.java new file mode 100644 index 00000000000..b2d52b253f2 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/task/TasksTest.java @@ -0,0 +1,90 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.task; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.sonar.api.task.Task; +import org.sonar.api.task.TaskDefinition; + +import static org.assertj.core.api.Assertions.assertThat; + +public class TasksTest { + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Test + public void should_get_definitions() { + Tasks tasks = new Tasks(new TaskDefinition[] {ScanTask.DEFINITION, ListTask.DEFINITION}); + assertThat(tasks.definitions()).hasSize(2); + } + + @Test + public void should_get_definition_by_key() { + Tasks tasks = new Tasks(new TaskDefinition[] {ScanTask.DEFINITION, ListTask.DEFINITION}); + tasks.start(); + assertThat(tasks.definition(ListTask.DEFINITION.key())).isEqualTo(ListTask.DEFINITION); + } + + @Test + public void should_return_null_if_task_not_found() { + Tasks tasks = new Tasks(new TaskDefinition[] {ScanTask.DEFINITION, ListTask.DEFINITION}); + + assertThat(tasks.definition("not-exists")).isNull(); + } + + @Test + public void should_fail_on_duplicated_keys() { + thrown.expect(IllegalStateException.class); + thrown.expectMessage("Task 'foo' is declared twice"); + + new Tasks(new TaskDefinition[] { + TaskDefinition.builder().key("foo").taskClass(FakeTask1.class).description("foo1").build(), + TaskDefinition.builder().key("foo").taskClass(FakeTask2.class).description("foo2").build() + }); + } + + @Test + public void should_fail_on_duplicated_class() { + Tasks tasks = new Tasks(new TaskDefinition[] { + TaskDefinition.builder().key("foo1").taskClass(FakeTask1.class).description("foo1").build(), + TaskDefinition.builder().key("foo2").taskClass(FakeTask1.class).description("foo1").build() + }); + + thrown.expect(IllegalStateException.class); + thrown.expectMessage("Task 'org.sonar.batch.task.TasksTest$FakeTask1' is defined twice: first by 'foo1' and then by 'foo2'"); + + tasks.start(); + } + + private static class FakeTask1 implements Task { + public void execute() { + } + } + + private static class FakeTask2 implements Task { + public void execute() { + } + + } + +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/util/BatchUtilsTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/util/BatchUtilsTest.java new file mode 100644 index 00000000000..8de6036b5e3 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/util/BatchUtilsTest.java @@ -0,0 +1,51 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.util; + +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class BatchUtilsTest { + + @Test + public void encodeForUrl() throws Exception { + assertThat(BatchUtils.encodeForUrl(null)).isEqualTo(""); + assertThat(BatchUtils.encodeForUrl("")).isEqualTo(""); + assertThat(BatchUtils.encodeForUrl("foo")).isEqualTo("foo"); + assertThat(BatchUtils.encodeForUrl("foo&bar")).isEqualTo("foo%26bar"); + } + + @Test + + public void testDescribe() { + Object withToString = new Object() { + @Override + public String toString() { + return "desc"; + } + }; + + Object withoutToString = new Object(); + + assertThat(BatchUtils.describe(withToString)).isEqualTo(("desc")); + assertThat(BatchUtils.describe(withoutToString)).isEqualTo("java.lang.Object"); + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/util/ProgressReportTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/util/ProgressReportTest.java new file mode 100644 index 00000000000..b21ad736fde --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/util/ProgressReportTest.java @@ -0,0 +1,90 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.util; + +import java.util.Set; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.sonar.api.utils.log.LogTester; + +import static org.assertj.core.api.Assertions.assertThat; + +public class ProgressReportTest { + private static final String THREAD_NAME = "progress"; + private ProgressReport progressReport; + + @Rule + public LogTester logTester = new LogTester(); + + @Before + public void setUp() { + progressReport = new ProgressReport(THREAD_NAME, 100); + } + + @Test + public void die_on_stop() { + progressReport.start("start"); + assertThat(isThreadAlive(THREAD_NAME)).isTrue(); + progressReport.stop("stop"); + assertThat(isThreadAlive(THREAD_NAME)).isFalse(); + } + + @Test + public void do_not_block_app() { + progressReport.start("start"); + assertThat(isDaemon(THREAD_NAME)).isTrue(); + progressReport.stop("stop"); + } + + @Test + public void do_log() { + progressReport.start("start"); + progressReport.message("Some message"); + try { + Thread.sleep(200); + } catch (InterruptedException e) { + // Ignore + } + progressReport.stop("stop"); + assertThat(logTester.logs()).contains("Some message"); + } + + private static boolean isDaemon(String name) { + Thread t = getThread(name); + return (t != null) && t.isDaemon(); + } + + private static boolean isThreadAlive(String name) { + Thread t = getThread(name); + return (t != null) && t.isAlive(); + } + + private static Thread getThread(String name) { + Set<Thread> threads = Thread.getAllStackTraces().keySet(); + + for (Thread t : threads) { + if (t.getName().equals(name)) { + return t; + } + } + return null; + } +} |