From b0058ce4110d6311c9fe3f81252da28883de1c43 Mon Sep 17 00:00:00 2001 From: Duarte Meneses Date: Fri, 15 Jul 2022 14:26:34 -0500 Subject: [PATCH] SONAR-17044 SONAR-17043 SONAR-17042 Optimize Compute Engine issue tracking and persisting of measures where file is marked as unchanged --- .../main/java/org/sonar/xoo/XooPlugin.java | 1 - .../scan/SpringProjectScanContainer.java | 4 + .../sensor/ExecutingSensorContext.java | 39 ++++++ .../scanner/sensor/ModuleSensorContext.java | 5 +- .../scanner/sensor/ProjectSensorContext.java | 7 +- .../sensor/ProjectSensorsExecutor.java | 17 +-- .../org/sonar/scanner/sensor/SensorId.java | 72 +++++++++++ .../scanner/sensor/UnchangedFilesHandler.java | 72 +++++++++++ .../sensor/ModuleSensorContextTest.java | 46 ++++--- .../sonar/scanner/sensor/SensorIdTest.java | 63 ++++++++++ .../sensor/UnchangedFilesHandlerTest.java | 119 ++++++++++++++++++ 11 files changed, 407 insertions(+), 38 deletions(-) create mode 100644 sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/ExecutingSensorContext.java create mode 100644 sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/SensorId.java create mode 100644 sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/UnchangedFilesHandler.java create mode 100644 sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/SensorIdTest.java create mode 100644 sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/UnchangedFilesHandlerTest.java diff --git a/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/XooPlugin.java b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/XooPlugin.java index ef3d0cce1e5..3435aab97db 100644 --- a/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/XooPlugin.java +++ b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/XooPlugin.java @@ -46,7 +46,6 @@ import org.sonar.xoo.rule.HasTagSensor; import org.sonar.xoo.rule.hotspot.HotspotWithSingleContextSensor; import org.sonar.xoo.rule.hotspot.HotspotWithoutContextSensor; import org.sonar.xoo.rule.hotspot.HotspotWithContextsSensor; -import org.sonar.xoo.rule.HotspotSensor; import org.sonar.xoo.rule.MarkAsUnchangedSensor; import org.sonar.xoo.rule.MultilineIssuesSensor; import org.sonar.xoo.rule.NoSonarSensor; diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/SpringProjectScanContainer.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/SpringProjectScanContainer.java index ca4a9cae98a..30aeaf63a9d 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/SpringProjectScanContainer.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/SpringProjectScanContainer.java @@ -126,10 +126,12 @@ import org.sonar.scanner.scm.ScmConfiguration; import org.sonar.scanner.scm.ScmPublisher; import org.sonar.scanner.scm.ScmRevisionImpl; import org.sonar.scanner.sensor.DefaultSensorStorage; +import org.sonar.scanner.sensor.ExecutingSensorContext; import org.sonar.scanner.sensor.ProjectSensorContext; import org.sonar.scanner.sensor.ProjectSensorExtensionDictionary; import org.sonar.scanner.sensor.ProjectSensorOptimizer; import org.sonar.scanner.sensor.ProjectSensorsExecutor; +import org.sonar.scanner.sensor.UnchangedFilesHandler; import org.sonar.scm.git.GitScmSupport; import org.sonar.scm.svn.SvnScmSupport; @@ -275,7 +277,9 @@ public class SpringProjectScanContainer extends SpringComponentContainer { ProjectSensorContext.class, ProjectSensorOptimizer.class, ProjectSensorsExecutor.class, + ExecutingSensorContext.class, ProjectSensorExtensionDictionary.class, + UnchangedFilesHandler.class, // Filesystem DefaultProjectFileSystem.class, diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/ExecutingSensorContext.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/ExecutingSensorContext.java new file mode 100644 index 00000000000..9c2e78518e3 --- /dev/null +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/ExecutingSensorContext.java @@ -0,0 +1,39 @@ +/* + * SonarQube + * Copyright (C) 2009-2022 SonarSource SA + * mailto:info 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.scanner.sensor; + +import javax.annotation.CheckForNull; + +public class ExecutingSensorContext { + private SensorId sensor; + + public void setSensorExecuting(SensorId sensor) { + this.sensor = sensor; + } + + public void clearExecutingSensor() { + this.sensor = null; + } + + @CheckForNull + public SensorId getSensorExecuting() { + return sensor; + } +} diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/ModuleSensorContext.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/ModuleSensorContext.java index a573f94ff0e..ac6dd0f09fc 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/ModuleSensorContext.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/ModuleSensorContext.java @@ -39,8 +39,9 @@ public class ModuleSensorContext extends ProjectSensorContext { public ModuleSensorContext(DefaultInputProject project, InputModule module, Configuration config, Settings mutableModuleSettings, FileSystem fs, ActiveRules activeRules, DefaultSensorStorage sensorStorage, SonarRuntime sonarRuntime, BranchConfiguration branchConfiguration, - WriteCache writeCache, ReadCache readCache, AnalysisCacheEnabled analysisCacheEnabled) { - super(project, config, mutableModuleSettings, fs, activeRules, sensorStorage, sonarRuntime, branchConfiguration, writeCache, readCache, analysisCacheEnabled); + WriteCache writeCache, ReadCache readCache, AnalysisCacheEnabled analysisCacheEnabled, UnchangedFilesHandler unchangedFilesHandler) { + super(project, config, mutableModuleSettings, fs, activeRules, sensorStorage, sonarRuntime, branchConfiguration, writeCache, readCache, analysisCacheEnabled, + unchangedFilesHandler); this.module = module; } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/ProjectSensorContext.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/ProjectSensorContext.java index 4c72730b94c..8a24b79956d 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/ProjectSensorContext.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/ProjectSensorContext.java @@ -71,13 +71,14 @@ public class ProjectSensorContext implements SensorContext { private final SonarRuntime sonarRuntime; private final Configuration config; private final boolean skipUnchangedFiles; + private final UnchangedFilesHandler unchangedFilesHandler; private final WriteCache writeCache; private final ReadCache readCache; private final AnalysisCacheEnabled analysisCacheEnabled; public ProjectSensorContext(DefaultInputProject project, Configuration config, Settings mutableSettings, FileSystem fs, ActiveRules activeRules, DefaultSensorStorage sensorStorage, SonarRuntime sonarRuntime, BranchConfiguration branchConfiguration, WriteCache writeCache, ReadCache readCache, - AnalysisCacheEnabled analysisCacheEnabled) { + AnalysisCacheEnabled analysisCacheEnabled, UnchangedFilesHandler unchangedFilesHandler) { this.project = project; this.config = config; this.mutableSettings = mutableSettings; @@ -89,6 +90,7 @@ public class ProjectSensorContext implements SensorContext { this.readCache = readCache; this.analysisCacheEnabled = analysisCacheEnabled; this.skipUnchangedFiles = branchConfiguration.isPullRequest(); + this.unchangedFilesHandler = unchangedFilesHandler; } @Override @@ -194,8 +196,7 @@ public class ProjectSensorContext implements SensorContext { @Override public void markAsUnchanged(InputFile inputFile) { - DefaultInputFile defaultInputFile = (DefaultInputFile) inputFile; - defaultInputFile.setMarkedAsUnchanged(true); + unchangedFilesHandler.markAsUnchanged((DefaultInputFile) inputFile); } @Override diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/ProjectSensorsExecutor.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/ProjectSensorsExecutor.java index d68c24d5b70..ae9c464b5ee 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/ProjectSensorsExecutor.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/ProjectSensorsExecutor.java @@ -31,10 +31,12 @@ public class ProjectSensorsExecutor { private static final Profiler profiler = Profiler.create(LOG); private final ProjectSensorExtensionDictionary selector; private final ScannerPluginRepository pluginRepo; + private final ExecutingSensorContext executingSensorCtx; - public ProjectSensorsExecutor(ProjectSensorExtensionDictionary selector, ScannerPluginRepository pluginRepo) { + public ProjectSensorsExecutor(ProjectSensorExtensionDictionary selector, ScannerPluginRepository pluginRepo, ExecutingSensorContext executingSensorCtx) { this.selector = selector; this.pluginRepo = pluginRepo; + this.executingSensorCtx = executingSensorCtx; } public void execute() { @@ -44,20 +46,19 @@ public class ProjectSensorsExecutor { .map(Object::toString) .collect(Collectors.joining(" -> "))); for (ProjectSensorWrapper sensor : sensors) { - String sensorName = getSensorName(sensor); - profiler.startInfo("Sensor " + sensorName); + SensorId sensorId = getSensorId(sensor); + executingSensorCtx.setSensorExecuting(sensorId); + profiler.startInfo("Sensor " + sensorId); sensor.analyse(); profiler.stopInfo(); + executingSensorCtx.clearExecutingSensor(); } } - private String getSensorName(ProjectSensorWrapper sensor) { + private SensorId getSensorId(ProjectSensorWrapper sensor) { ClassLoader cl = getSensorClassLoader(sensor); String pluginKey = pluginRepo.getPluginKey(cl); - if (pluginKey != null) { - return sensor.toString() + " [" + pluginKey + "]"; - } - return sensor.toString(); + return new SensorId(pluginKey, sensor.toString()); } private static ClassLoader getSensorClassLoader(ProjectSensorWrapper sensor) { diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/SensorId.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/SensorId.java new file mode 100644 index 00000000000..0166430c128 --- /dev/null +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/SensorId.java @@ -0,0 +1,72 @@ +/* + * SonarQube + * Copyright (C) 2009-2022 SonarSource SA + * mailto:info 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.scanner.sensor; + +import java.util.Objects; +import javax.annotation.Nullable; + +import static org.sonar.api.utils.Preconditions.checkNotNull; + +public class SensorId { + private final String sensorName; + @Nullable + private final String pluginKey; + + public SensorId(@Nullable String pluginKey, String sensorName) { + checkNotNull(sensorName); + this.pluginKey = pluginKey; + this.sensorName = sensorName; + } + + @Nullable + public String getPluginKey() { + return pluginKey; + } + + public String getSensorName() { + return sensorName; + } + + @Override + public String toString() { + if (pluginKey == null) { + return sensorName; + } else { + return sensorName + " [" + pluginKey + "]"; + } + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + SensorId sensorId = (SensorId) o; + return Objects.equals(sensorName, sensorId.sensorName) && Objects.equals(pluginKey, sensorId.pluginKey); + } + + @Override + public int hashCode() { + return Objects.hash(sensorName, pluginKey); + } +} diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/UnchangedFilesHandler.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/UnchangedFilesHandler.java new file mode 100644 index 00000000000..29803dbee67 --- /dev/null +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/UnchangedFilesHandler.java @@ -0,0 +1,72 @@ +/* + * SonarQube + * Copyright (C) 2009-2022 SonarSource SA + * mailto:info 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.scanner.sensor; + +import java.util.Objects; +import java.util.Set; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.config.Configuration; +import org.sonar.api.utils.log.Logger; +import org.sonar.api.utils.log.Loggers; +import org.sonar.scanner.scan.branch.BranchConfiguration; + +public class UnchangedFilesHandler { + private static final Logger LOG = Loggers.get(UnchangedFilesHandler.class); + private static final Set ENABLED_SENSORS = Set.of( + new SensorId("cpp", "CFamily"), + new SensorId("cobol", "CobolSquidSensor")); + private static final String ENABLE_PROPERTY_KEY = "sonar.unchangedFiles.optimize"; + private final boolean featureActive; + private final ExecutingSensorContext executingSensorContext; + + public UnchangedFilesHandler(Configuration configuration, BranchConfiguration branchConfiguration, ExecutingSensorContext executingSensorContext) { + this.executingSensorContext = executingSensorContext; + this.featureActive = getFeatureActivationStatus(configuration, branchConfiguration); + } + + private static boolean getFeatureActivationStatus(Configuration configuration, BranchConfiguration branchConfiguration) { + boolean isPropertyEnabled = configuration.getBoolean(ENABLE_PROPERTY_KEY).orElse(false); + if (!isPropertyEnabled) { + return false; + } + if (branchConfiguration.isPullRequest() || !Objects.equals(branchConfiguration.branchName(), branchConfiguration.referenceBranchName())) { + LOG.debug("Optimization for unchanged files not enabled because it's not an analysis of a branch with a previous analysis"); + return false; + } + LOG.info("Optimization for unchanged files enabled"); + return true; + } + + public void markAsUnchanged(DefaultInputFile file) { + if (isFeatureActive()) { + if (file.status() != InputFile.Status.SAME) { + LOG.error("File '{}' was marked as unchanged but its status is {}", file.getProjectRelativePath(), file.status()); + } else { + LOG.debug("File '{}' marked as unchanged", file.getProjectRelativePath()); + file.setMarkedAsUnchanged(true); + } + } + } + + private boolean isFeatureActive() { + return featureActive && ENABLED_SENSORS.contains(executingSensorContext.getSensorExecuting()); + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/ModuleSensorContextTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/ModuleSensorContextTest.java index fb719656262..5a3061e9c8f 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/ModuleSensorContextTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/ModuleSensorContextTest.java @@ -28,13 +28,12 @@ import org.sonar.api.SonarQubeSide; import org.sonar.api.SonarRuntime; import org.sonar.api.batch.fs.InputModule; import org.sonar.api.batch.fs.internal.DefaultFileSystem; +import org.sonar.api.batch.fs.internal.DefaultInputFile; import org.sonar.api.batch.fs.internal.DefaultInputProject; -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.config.internal.MapSettings; import org.sonar.api.internal.SonarRuntimeImpl; -import org.sonar.api.measures.CoreMetrics; import org.sonar.api.utils.Version; import org.sonar.scanner.cache.AnalysisCacheEnabled; import org.sonar.scanner.cache.ReadCacheImpl; @@ -43,6 +42,7 @@ import org.sonar.scanner.scan.branch.BranchConfiguration; 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 ModuleSensorContextTest { @@ -50,37 +50,27 @@ public class ModuleSensorContextTest { @Rule public TemporaryFolder temp = new TemporaryFolder(); - private ActiveRules activeRules; + private final ActiveRules activeRules = new ActiveRulesBuilder().build(); + private final MapSettings settings = new MapSettings(); + private final DefaultSensorStorage sensorStorage = mock(DefaultSensorStorage.class); + private final BranchConfiguration branchConfiguration = mock(BranchConfiguration.class); + private final WriteCacheImpl writeCache = mock(WriteCacheImpl.class); + private final ReadCacheImpl readCache = mock(ReadCacheImpl.class); + private final AnalysisCacheEnabled analysisCacheEnabled = mock(AnalysisCacheEnabled.class); + private final UnchangedFilesHandler unchangedFilesHandler = mock(UnchangedFilesHandler.class); + private final SonarRuntime runtime = SonarRuntimeImpl.forSonarQube(Version.parse("5.5"), SonarQubeSide.SCANNER, SonarEdition.COMMUNITY); private DefaultFileSystem fs; private ModuleSensorContext adaptor; - private MapSettings settings; - private DefaultSensorStorage sensorStorage; - private SonarRuntime runtime; - private BranchConfiguration branchConfiguration; - private WriteCacheImpl writeCache; - private ReadCacheImpl readCache; - private AnalysisCacheEnabled analysisCacheEnabled; @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 MapSettings(); - sensorStorage = mock(DefaultSensorStorage.class); - branchConfiguration = mock(BranchConfiguration.class); - writeCache = mock(WriteCacheImpl.class); - readCache = mock(ReadCacheImpl.class); - analysisCacheEnabled = mock(AnalysisCacheEnabled.class); - runtime = SonarRuntimeImpl.forSonarQube(Version.parse("5.5"), SonarQubeSide.SCANNER, SonarEdition.COMMUNITY); + adaptor = new ModuleSensorContext(mock(DefaultInputProject.class), mock(InputModule.class), settings.asConfig(), settings, fs, activeRules, sensorStorage, runtime, + branchConfiguration, writeCache, readCache, analysisCacheEnabled, unchangedFilesHandler); } @Test public void shouldProvideComponents() { - adaptor = new ModuleSensorContext(mock(DefaultInputProject.class), mock(InputModule.class), settings.asConfig(), settings, fs, activeRules, sensorStorage, runtime, - branchConfiguration, writeCache, readCache, analysisCacheEnabled); assertThat(adaptor.activeRules()).isEqualTo(activeRules); assertThat(adaptor.fileSystem()).isEqualTo(fs); assertThat(adaptor.getSonarQubeVersion()).isEqualTo(Version.parse("5.5")); @@ -99,11 +89,19 @@ public class ModuleSensorContextTest { assertThat(adaptor.newSignificantCode()).isNotNull(); } + @Test + public void should_delegate_to_unchanged_files_handler() { + DefaultInputFile defaultInputFile = mock(DefaultInputFile.class); + adaptor.markAsUnchanged(defaultInputFile); + + verify(unchangedFilesHandler).markAsUnchanged(defaultInputFile); + } + @Test public void pull_request_can_skip_unchanged_files() { when(branchConfiguration.isPullRequest()).thenReturn(true); adaptor = new ModuleSensorContext(mock(DefaultInputProject.class), mock(InputModule.class), settings.asConfig(), settings, fs, activeRules, sensorStorage, runtime, - branchConfiguration, writeCache, readCache, analysisCacheEnabled); + branchConfiguration, writeCache, readCache, analysisCacheEnabled, unchangedFilesHandler); assertThat(adaptor.canSkipUnchangedFiles()).isTrue(); } diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/SensorIdTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/SensorIdTest.java new file mode 100644 index 00000000000..2c7053879ef --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/SensorIdTest.java @@ -0,0 +1,63 @@ +/* + * SonarQube + * Copyright (C) 2009-2022 SonarSource SA + * mailto:info 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.scanner.sensor; + +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +public class SensorIdTest { + @Test + public void equals_and_hashCode_depend_on_all_fields() { + SensorId s1 = new SensorId("a", "b"); + SensorId s2 = new SensorId("a", "b"); + SensorId s3 = new SensorId(null, "b"); + SensorId s4 = new SensorId("a", "a"); + + assertThat(s1) + .isEqualTo(s1) + .isEqualTo(s2) + .isNotEqualTo(s3) + .isNotEqualTo(s4) + .hasSameHashCodeAs(s2); + } + + @Test + public void constructor_fails_if_sensorName_is_null() { + assertThatThrownBy(() -> new SensorId("p1", null)).isInstanceOf(NullPointerException.class); + } + + @Test + public void getters_are_correct() { + SensorId s1 = new SensorId("a", "b"); + assertThat(s1.getSensorName()).isEqualTo("b"); + assertThat(s1.getPluginKey()).isEqualTo("a"); + } + + @Test + public void toString_supports_all_values() { + SensorId s = new SensorId(null, "a"); + assertThat(s).hasToString("a"); + + s = new SensorId("a", "b"); + assertThat(s).hasToString("b [a]"); + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/UnchangedFilesHandlerTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/UnchangedFilesHandlerTest.java new file mode 100644 index 00000000000..243ab9f3b76 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/UnchangedFilesHandlerTest.java @@ -0,0 +1,119 @@ +/* + * SonarQube + * Copyright (C) 2009-2022 SonarSource SA + * mailto:info 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.scanner.sensor; + +import javax.annotation.Nullable; +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.Configuration; +import org.sonar.api.config.internal.MapSettings; +import org.sonar.api.utils.log.LogTester; +import org.sonar.scanner.scan.branch.BranchConfiguration; +import org.sonar.scanner.scan.branch.DefaultBranchConfiguration; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; +import static org.mockito.Mockito.when; + +public class UnchangedFilesHandlerTest { + private final ExecutingSensorContext executingSensorContext = new ExecutingSensorContext(); + private final Configuration enabledConfig = new MapSettings().setProperty("sonar.unchangedFiles.optimize", true).asConfig(); + private final DefaultInputFile file = mock(DefaultInputFile.class); + private final BranchConfiguration defaultBranchConfig = new DefaultBranchConfiguration(); + + @Rule + public LogTester logTester = new LogTester(); + + @Test + public void log_if_active() { + new UnchangedFilesHandler(enabledConfig, defaultBranchConfig, executingSensorContext); + assertThat(logTester.logs()).contains("Optimization for unchanged files enabled"); + } + + @Test + public void not_active_if_its_pr() { + BranchConfiguration prConfig = branchConfiguration(null, null, true); + UnchangedFilesHandler handler = new UnchangedFilesHandler(enabledConfig, prConfig, executingSensorContext); + assertThat(logTester.logs()).contains("Optimization for unchanged files not enabled because it's not an analysis of a branch with a previous analysis"); + + handler.markAsUnchanged(file); + verifyNoInteractions(file); + } + + @Test + public void not_active_if_using_different_reference() { + BranchConfiguration differentRefConfig = branchConfiguration("a", "b", false); + UnchangedFilesHandler handler = new UnchangedFilesHandler(enabledConfig, differentRefConfig, executingSensorContext); + assertThat(logTester.logs()).contains("Optimization for unchanged files not enabled because it's not an analysis of a branch with a previous analysis"); + + handler.markAsUnchanged(file); + verifyNoInteractions(file); + } + + @Test + public void not_active_if_property_not_defined() { + UnchangedFilesHandler handler = new UnchangedFilesHandler(new MapSettings().asConfig(), defaultBranchConfig, executingSensorContext); + handler.markAsUnchanged(file); + verifyNoInteractions(file); + assertThat(logTester.logs()).isEmpty(); + } + + @Test + public void mark_file_if_sensor_is_enabled() { + when(file.status()).thenReturn(InputFile.Status.SAME); + executingSensorContext.setSensorExecuting(new SensorId("cpp", "CFamily")); + UnchangedFilesHandler handler = new UnchangedFilesHandler(enabledConfig, defaultBranchConfig, executingSensorContext); + + handler.markAsUnchanged(file); + verify(file).setMarkedAsUnchanged(true); + } + + @Test + public void dont_mark_file_if_sensor_is_not_enabled() { + executingSensorContext.setSensorExecuting(new SensorId("cpp", "other")); + UnchangedFilesHandler handler = new UnchangedFilesHandler(enabledConfig, defaultBranchConfig, executingSensorContext); + + handler.markAsUnchanged(file); + verifyNoInteractions(file); + } + + @Test + public void dont_mark_file_is_status_is_not_same() { + when(file.status()).thenReturn(InputFile.Status.CHANGED); + executingSensorContext.setSensorExecuting(new SensorId("cpp", "CFamily")); + UnchangedFilesHandler handler = new UnchangedFilesHandler(enabledConfig, defaultBranchConfig, executingSensorContext); + + handler.markAsUnchanged(file); + verify(file, never()).setMarkedAsUnchanged(true); + } + + private BranchConfiguration branchConfiguration(@Nullable String branchName, @Nullable String referenceName, boolean isPullRequest) { + BranchConfiguration branchConfiguration = mock(BranchConfiguration.class); + when(branchConfiguration.referenceBranchName()).thenReturn(referenceName); + when(branchConfiguration.branchName()).thenReturn(branchName); + when(branchConfiguration.isPullRequest()).thenReturn(isPullRequest); + return branchConfiguration; + } +} -- 2.39.5