diff options
Diffstat (limited to 'sonar-scanner-engine/src/test')
20 files changed, 982 insertions, 274 deletions
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/externalissue/sarif/RulesSeverityDetectorTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/externalissue/sarif/RulesSeverityDetectorTest.java index 5091dcf5a3a..f5d88016944 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/externalissue/sarif/RulesSeverityDetectorTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/externalissue/sarif/RulesSeverityDetectorTest.java @@ -22,12 +22,16 @@ package org.sonar.scanner.externalissue.sarif; import java.util.List; import java.util.Map; import java.util.Set; +import javax.annotation.Nullable; import org.assertj.core.api.Assertions; import org.assertj.core.groups.Tuple; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.NullAndEmptySource; import org.slf4j.event.Level; -import org.sonar.api.testfixtures.log.LogTester; +import org.sonar.api.testfixtures.log.LogTesterJUnit5; import org.sonar.sarif.pojo.ReportingConfiguration; import org.sonar.sarif.pojo.ReportingDescriptor; import org.sonar.sarif.pojo.Result; @@ -44,12 +48,12 @@ import static org.sonar.sarif.pojo.Result.Level.WARNING; import static org.sonar.scanner.externalissue.sarif.ResultMapper.DEFAULT_IMPACT_SEVERITY; import static org.sonar.scanner.externalissue.sarif.ResultMapper.DEFAULT_SEVERITY; -public class RulesSeverityDetectorTest { +class RulesSeverityDetectorTest { private static final String DRIVER_NAME = "Test"; private static final String RULE_ID = "RULE_ID"; - @org.junit.Rule - public LogTester logTester = new LogTester().setLevel(Level.TRACE); + @RegisterExtension + private final LogTesterJUnit5 logTester = new LogTesterJUnit5(); private final Run run = mock(Run.class); private final ReportingDescriptor rule = mock(ReportingDescriptor.class); @@ -59,8 +63,8 @@ public class RulesSeverityDetectorTest { private final ToolComponent extension = mock(ToolComponent.class); private final ReportingConfiguration defaultConfiguration = mock(ReportingConfiguration.class); - @Before - public void setUp() { + @BeforeEach + void setUp() { when(run.getResults()).thenReturn(List.of(result)); when(run.getTool()).thenReturn(tool); when(tool.getDriver()).thenReturn(driver); @@ -68,8 +72,8 @@ public class RulesSeverityDetectorTest { // We keep this test for backward compatibility until we remove the deprecated severity @Test - public void detectRulesSeverities_detectsCorrectlyResultDefinedRuleSeverities() { - Run run = mockResultDefinedRuleSeverities(); + void detectRulesSeverities_detectsCorrectlyResultDefinedRuleSeverities() { + mockResultDefinedRuleSeverities(); Map<String, Result.Level> rulesSeveritiesByRuleId = RulesSeverityDetector.detectRulesSeverities(run, DRIVER_NAME); @@ -78,8 +82,8 @@ public class RulesSeverityDetectorTest { } @Test - public void detectRulesSeveritiesForNewTaxonomy_shouldReturnsEmptyMapAndLogsWarning_whenOnlyResultDefinedRuleSeverities() { - Run run = mockResultDefinedRuleSeverities(); + void detectRulesSeveritiesForNewTaxonomy_shouldReturnsEmptyMapAndLogsWarning_whenOnlyResultDefinedRuleSeverities() { + mockResultDefinedRuleSeverities(); Map<String, Result.Level> rulesSeveritiesByRuleId = RulesSeverityDetector.detectRulesSeveritiesForNewTaxonomy(run, DRIVER_NAME); @@ -88,8 +92,8 @@ public class RulesSeverityDetectorTest { } @Test - public void detectRulesSeverities_detectsCorrectlyDriverDefinedRuleSeverities() { - Run run = mockDriverDefinedRuleSeverities(); + void detectRulesSeverities_detectsCorrectlyDriverDefinedRuleSeverities() { + mockDriverDefinedRuleSeverities(); Map<String, Result.Level> rulesSeveritiesByRuleId = RulesSeverityDetector.detectRulesSeveritiesForNewTaxonomy(run, DRIVER_NAME); @@ -103,9 +107,13 @@ public class RulesSeverityDetectorTest { assertDetectedRuleSeverities(rulesSeveritiesByRuleId, tuple(RULE_ID, WARNING)); } - @Test - public void detectRulesSeverities_detectsCorrectlyExtensionsDefinedRuleSeverities() { - Run run = mockExtensionsDefinedRuleSeverities(); + + + @ParameterizedTest + @NullAndEmptySource + void detectRulesSeverities_detectsCorrectlyExtensionsDefinedRuleSeverities(@Nullable Set<ReportingDescriptor> rules) { + when(driver.getRules()).thenReturn(rules); + mockExtensionsDefinedRuleSeverities(); Map<String, Result.Level> rulesSeveritiesByRuleId = RulesSeverityDetector.detectRulesSeveritiesForNewTaxonomy(run, DRIVER_NAME); @@ -120,8 +128,8 @@ public class RulesSeverityDetectorTest { } @Test - public void detectRulesSeverities_returnsEmptyMapAndLogsWarning_whenUnableToDetectSeverities() { - Run run = mockUnsupportedRuleSeveritiesDefinition(); + void detectRulesSeverities_returnsEmptyMapAndLogsWarning_whenUnableToDetectSeverities() { + mockUnsupportedRuleSeveritiesDefinition(); Map<String, Result.Level> rulesSeveritiesByRuleId = RulesSeverityDetector.detectRulesSeveritiesForNewTaxonomy(run, DRIVER_NAME); @@ -135,38 +143,33 @@ public class RulesSeverityDetectorTest { assertDetectedRuleSeverities(rulesSeveritiesByRuleId); } - private Run mockResultDefinedRuleSeverities() { + private void mockResultDefinedRuleSeverities() { when(run.getResults()).thenReturn(List.of(result)); when(result.getLevel()).thenReturn(WARNING); when(result.getRuleId()).thenReturn(RULE_ID); - return run; } - private Run mockDriverDefinedRuleSeverities() { + private void mockDriverDefinedRuleSeverities() { when(driver.getRules()).thenReturn(Set.of(rule)); when(rule.getId()).thenReturn(RULE_ID); when(rule.getDefaultConfiguration()).thenReturn(defaultConfiguration); when(defaultConfiguration.getLevel()).thenReturn(ReportingConfiguration.Level.WARNING); - return run; } - private Run mockExtensionsDefinedRuleSeverities() { - when(driver.getRules()).thenReturn(Set.of()); + private void mockExtensionsDefinedRuleSeverities() { when(tool.getExtensions()).thenReturn(Set.of(extension)); when(extension.getRules()).thenReturn(Set.of(rule)); when(rule.getId()).thenReturn(RULE_ID); when(rule.getDefaultConfiguration()).thenReturn(defaultConfiguration); when(defaultConfiguration.getLevel()).thenReturn(ReportingConfiguration.Level.WARNING); - return run; } - private Run mockUnsupportedRuleSeveritiesDefinition() { + private void mockUnsupportedRuleSeveritiesDefinition() { when(run.getTool()).thenReturn(tool); when(tool.getDriver()).thenReturn(driver); when(driver.getRules()).thenReturn(Set.of()); when(tool.getExtensions()).thenReturn(Set.of(extension)); when(extension.getRules()).thenReturn(Set.of()); - return run; } private void assertNoLogs() { diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/externalissue/sarif/RunMapperTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/externalissue/sarif/RunMapperTest.java index 90ddfadd9f3..164787f8cde 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/externalissue/sarif/RunMapperTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/externalissue/sarif/RunMapperTest.java @@ -22,19 +22,19 @@ package org.sonar.scanner.externalissue.sarif; import java.util.List; import java.util.Map; import java.util.Set; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.extension.RegisterExtension; import org.mockito.Answers; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.MockedStatic; -import org.mockito.junit.MockitoJUnitRunner; +import org.mockito.junit.jupiter.MockitoExtension; import org.slf4j.event.Level; import org.sonar.api.batch.sensor.issue.NewExternalIssue; import org.sonar.api.batch.sensor.rule.NewAdHocRule; -import org.sonar.api.testfixtures.log.LogTester; +import org.sonar.api.testfixtures.log.LogTesterJUnit5; import org.sonar.sarif.pojo.ReportingDescriptor; import org.sonar.sarif.pojo.Result; import org.sonar.sarif.pojo.Run; @@ -44,13 +44,14 @@ import org.sonar.scanner.externalissue.sarif.RunMapper.RunMapperResult; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; import static org.assertj.core.api.Assertions.assertThatNoException; +import static org.mockito.Mockito.lenient; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mockStatic; import static org.mockito.Mockito.when; import static org.sonar.sarif.pojo.Result.Level.WARNING; -@RunWith(MockitoJUnitRunner.class) -public class RunMapperTest { +@ExtendWith(MockitoExtension.class) +class RunMapperTest { private static final String TEST_DRIVER = "Test driver"; public static final String RULE_ID = "ruleId"; @@ -66,21 +67,21 @@ public class RunMapperTest { @Mock private ReportingDescriptor rule; - @Rule - public LogTester logTester = new LogTester(); + @RegisterExtension + public LogTesterJUnit5 logTester = new LogTesterJUnit5(); @InjectMocks private RunMapper runMapper; - @Before - public void setUp() { - when(run.getTool().getDriver().getName()).thenReturn(TEST_DRIVER); - when(run.getTool().getExtensions()).thenReturn(null); - when(rule.getId()).thenReturn(RULE_ID); + @BeforeEach + void setUp() { + lenient().when(run.getTool().getDriver().getName()).thenReturn(TEST_DRIVER); + lenient().when(run.getTool().getExtensions()).thenReturn(null); + lenient().when(rule.getId()).thenReturn(RULE_ID); } @Test - public void mapRun_shouldMapExternalIssues() { + void mapRun_shouldMapExternalIssues() { Result result1 = mock(Result.class); Result result2 = mock(Result.class); when(run.getResults()).thenReturn(List.of(result1, result2)); @@ -99,7 +100,7 @@ public class RunMapperTest { } @Test - public void mapRun_shouldMapExternalRules_whenDriverHasRulesAndNoExtensions() { + void mapRun_shouldMapExternalRules_whenDriverHasRulesAndNoExtensions() { when(run.getTool().getDriver().getRules()).thenReturn(Set.of(rule)); NewAdHocRule externalRule = mockMappedExternalRule(); @@ -115,7 +116,7 @@ public class RunMapperTest { } @Test - public void mapRun_shouldMapExternalRules_whenRulesInExtensions() { + void mapRun_shouldMapExternalRules_whenRulesInExtensions() { when(run.getTool().getDriver().getRules()).thenReturn(Set.of()); ToolComponent extension = mock(ToolComponent.class); when(extension.getRules()).thenReturn(Set.of(rule)); @@ -134,7 +135,7 @@ public class RunMapperTest { } @Test - public void mapRun_shouldNotFail_whenExtensionsDontHaveRules() { + void mapRun_shouldNotFail_whenExtensionsDontHaveRules() { when(run.getTool().getDriver().getRules()).thenReturn(Set.of(rule)); ToolComponent extension = mock(ToolComponent.class); when(extension.getRules()).thenReturn(null); @@ -149,7 +150,7 @@ public class RunMapperTest { } @Test - public void mapRun_shouldNotFail_whenExtensionsHaveEmptyRules() { + void mapRun_shouldNotFail_whenExtensionsHaveEmptyRules() { when(run.getTool().getDriver().getRules()).thenReturn(Set.of(rule)); ToolComponent extension = mock(ToolComponent.class); when(extension.getRules()).thenReturn(Set.of()); @@ -164,7 +165,7 @@ public class RunMapperTest { } @Test - public void mapRun_ifRunIsEmpty_returnsEmptyList() { + void mapRun_ifRunIsEmpty_returnsEmptyList() { when(run.getResults()).thenReturn(List.of()); RunMapperResult runMapperResult = runMapper.mapRun(run); @@ -173,7 +174,7 @@ public class RunMapperTest { } @Test - public void mapRun_ifExceptionThrownByResultMapper_logsThemAndContinueProcessing() { + void mapRun_ifExceptionThrownByResultMapper_logsThemAndContinueProcessing() { Result result1 = mock(Result.class); Result result2 = mock(Result.class); when(run.getResults()).thenReturn(List.of(result1, result2)); @@ -194,7 +195,7 @@ public class RunMapperTest { } @Test - public void mapRun_failsIfToolNotSet() { + void mapRun_failsIfToolNotSet() { when(run.getTool()).thenReturn(null); assertThatIllegalArgumentException() @@ -203,7 +204,7 @@ public class RunMapperTest { } @Test - public void mapRun_failsIfDriverNotSet() { + void mapRun_failsIfDriverNotSet() { when(run.getTool().getDriver()).thenReturn(null); assertThatIllegalArgumentException() @@ -212,7 +213,7 @@ public class RunMapperTest { } @Test - public void mapRun_failsIfDriverNameIsNotSet() { + void mapRun_failsIfDriverNameIsNotSet() { when(run.getTool().getDriver().getName()).thenReturn(null); assertThatIllegalArgumentException() @@ -220,6 +221,25 @@ public class RunMapperTest { .withMessage("The run does not have a tool driver name defined."); } + @Test + void mapRun_shouldNotFail_whenDriverRulesNullAndExtensionsRulesNotNull() { + when(run.getTool().getDriver().getRules()).thenReturn(null); + ToolComponent extension = mock(ToolComponent.class); + when(extension.getRules()).thenReturn(Set.of(rule)); + when(run.getTool().getExtensions()).thenReturn(Set.of(extension)); + NewAdHocRule expectedRule = mock(NewAdHocRule.class); + when(ruleMapper.mapRule(rule, TEST_DRIVER, WARNING, WARNING)).thenReturn(expectedRule); + + try (MockedStatic<RulesSeverityDetector> detector = mockStatic(RulesSeverityDetector.class)) { + detector.when(() -> RulesSeverityDetector.detectRulesSeverities(run, TEST_DRIVER)).thenReturn(Map.of(RULE_ID, WARNING)); + detector.when(() -> RulesSeverityDetector.detectRulesSeveritiesForNewTaxonomy(run, TEST_DRIVER)).thenReturn(Map.of(RULE_ID, WARNING)); + + RunMapperResult runMapperResult = runMapper.mapRun(run); + assertThat(runMapperResult.getNewAdHocRules()).hasSize(1); + assertThat(runMapperResult.getNewAdHocRules().get(0)).isEqualTo(expectedRule); + } + } + private NewExternalIssue mockMappedExternalIssue(Result result) { NewExternalIssue externalIssue = mock(NewExternalIssue.class); when(result.getRuleId()).thenReturn(RULE_ID); diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/sca/CliCacheServiceTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/sca/CliCacheServiceTest.java index 9193157e213..6615ba4e4e4 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/sca/CliCacheServiceTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/sca/CliCacheServiceTest.java @@ -29,6 +29,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardCopyOption; import org.apache.commons.io.FileUtils; +import org.apache.commons.lang.SystemUtils; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -161,7 +162,7 @@ class CliCacheServiceTest { WsTestUtil.mockException(scannerWsClient, e); assertThatThrownBy(underTest::cacheCli).isInstanceOf(IllegalStateException.class) - .hasMessageContaining("Unable to load CLI metadata"); + .hasMessageContaining("http error"); verify(telemetryCache).put("scanner.sca.get.cli.success", "false"); } @@ -187,14 +188,18 @@ class CliCacheServiceTest { FileUtils.writeStringToFile(existingFile, fileContent, Charset.defaultCharset()); assertThat(existingFile).exists(); - assertThat(existingFile.canExecute()).isFalse(); + if (!SystemUtils.IS_OS_WINDOWS) { + assertThat(existingFile.canExecute()).isFalse(); + } assertThat(FileUtils.readFileToString(existingFile, Charset.defaultCharset())).isEqualTo(fileContent); underTest.cacheCli(); WsTestUtil.verifyCall(scannerWsClient, CLI_WS_URL); assertThat(existingFile).exists(); - assertThat(existingFile.canExecute()).isFalse(); + if (!SystemUtils.IS_OS_WINDOWS) { + assertThat(existingFile.canExecute()).isFalse(); + } assertThat(FileUtils.readFileToString(existingFile, Charset.defaultCharset())).isEqualTo(fileContent); verify(telemetryCache).put("scanner.sca.get.cli.cache.hit", "true"); diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/sca/CliServiceTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/sca/CliServiceTest.java index 597fafa833c..33cf6c146e6 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/sca/CliServiceTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/sca/CliServiceTest.java @@ -27,7 +27,6 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.List; import java.util.Map; -import java.util.Optional; import org.apache.commons.lang3.SystemUtils; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -36,6 +35,7 @@ import org.junit.jupiter.api.extension.RegisterExtension; import org.junit.jupiter.api.io.TempDir; import org.mockito.MockedStatic; import org.sonar.api.batch.bootstrap.ProjectDefinition; +import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.fs.internal.DefaultInputModule; import org.sonar.api.batch.scm.ScmProvider; import org.sonar.api.platform.Server; @@ -44,6 +44,7 @@ import org.sonar.api.utils.System2; import org.sonar.core.util.ProcessWrapperFactory; import org.sonar.scanner.config.DefaultConfiguration; import org.sonar.scanner.repository.TelemetryCache; +import org.sonar.scanner.scan.filesystem.ProjectExclusionFilters; import org.sonar.scanner.scm.ScmConfiguration; import org.sonar.scm.git.GitScmProvider; import org.sonar.scm.git.JGitUtils; @@ -69,6 +70,7 @@ class CliServiceTest { ProcessWrapperFactory processWrapperFactory = mock(ProcessWrapperFactory.class, CALLS_REAL_METHODS); private MockedStatic<JGitUtils> jGitUtilsMock; DefaultConfiguration configuration = mock(DefaultConfiguration.class); + ProjectExclusionFilters projectExclusionFilters = mock(ProjectExclusionFilters.class); private CliService underTest; @@ -86,9 +88,11 @@ class CliServiceTest { jGitUtilsMock.when(() -> JGitUtils.getAllIgnoredPaths(any(Path.class))).thenReturn(List.of("ignored.txt")); when(server.getVersion()).thenReturn("1.0.0"); logTester.setLevel(INFO); - when(configuration.getBoolean("sonar.sca.debug")).thenReturn(Optional.of(true)); + when(projectExclusionFilters.getExclusionsConfig(InputFile.Type.MAIN)).thenReturn(new String[0]); + when(configuration.getStringArray(CliService.SCA_EXCLUSIONS_KEY)).thenReturn(new String[0]); + when(configuration.getStringArray(CliService.LEGACY_SCA_EXCLUSIONS_KEY)).thenReturn(new String[0]); - underTest = new CliService(processWrapperFactory, telemetryCache, System2.INSTANCE, server, scmConfiguration); + underTest = new CliService(processWrapperFactory, telemetryCache, System2.INSTANCE, server, scmConfiguration, projectExclusionFilters); } @AfterEach @@ -99,57 +103,55 @@ class CliServiceTest { } @Test - void generateZip_shouldCallProcessCorrectly_andRegisterTelemetry() throws IOException, URISyntaxException { + void generateManifestsArchive_shouldCallProcessCorrectly_andRegisterTelemetry() throws IOException, URISyntaxException { assertThat(rootModuleDir.resolve("test_file").toFile().createNewFile()).isTrue(); - when(configuration.getProperties()).thenReturn(Map.of("sonar.sca.recursiveManifestSearch", "true", CliService.EXCLUDED_MANIFESTS_PROP_KEY, "foo,bar,baz/**")); - when(configuration.get("sonar.sca.recursiveManifestSearch")).thenReturn(Optional.of("true")); - when(configuration.getStringArray(CliService.EXCLUDED_MANIFESTS_PROP_KEY)).thenReturn(new String[] {"foo", "bar", "baz/**"}); + when(configuration.getProperties()).thenReturn(Map.of(CliService.SCA_EXCLUSIONS_KEY, "foo,bar,baz/**")); + when(configuration.getStringArray(CliService.SCA_EXCLUSIONS_KEY)).thenReturn(new String[] {"foo", "bar", "baz/**"}); - File producedZip = underTest.generateManifestsZip(rootInputModule, scriptDir(), configuration); + File producedArchive = underTest.generateManifestsArchive(rootInputModule, scriptDir(), configuration); - assertThat(producedZip).exists(); + assertThat(producedArchive).exists(); var expectedArguments = List.of( "projects", "save-lockfiles", - "--zip", - "--zip-filename", - rootInputModule.getWorkDir().resolve("dependency-files.zip").toString(), + "--xz", + "--xz-filename", + rootInputModule.getWorkDir().resolve("dependency-files.tar.xz").toString(), "--directory", rootInputModule.getBaseDir().toString(), + "--recursive", "--exclude", - "foo,bar,baz/**,ignored.txt,.scannerwork/**", - "--debug"); + "foo,bar,baz/**,ignored.txt,.scannerwork/**"); assertThat(logTester.logs(INFO)) .contains("Arguments Passed In: " + String.join(" ", expectedArguments)) .contains("TIDELIFT_SKIP_UPDATE_CHECK=1") .contains("TIDELIFT_ALLOW_MANIFEST_FAILURES=1") - .contains("TIDELIFT_RECURSIVE_MANIFEST_SEARCH=true") - .contains("Generated manifests zip file: " + producedZip.getName()); + .contains("Generated manifests archive file: " + producedArchive.getName()); assertThat(telemetryCache.getAll()).containsKey("scanner.sca.execution.cli.duration").isNotNull(); assertThat(telemetryCache.getAll()).containsEntry("scanner.sca.execution.cli.success", "true"); } @Test - void generateZip_whenDebugLogLevelAndScaDebugNotEnabled_shouldWriteDebugLogsToDebugStream() throws IOException, URISyntaxException { + void generateManifestsArchive_whenDebugLogLevelAndScaDebugNotEnabled_shouldWriteDebugLogsToDebugStream() throws IOException, URISyntaxException { logTester.setLevel(DEBUG); - when(configuration.getBoolean("sonar.sca.debug")).thenReturn(Optional.of(false)); assertThat(rootModuleDir.resolve("test_file").toFile().createNewFile()).isTrue(); - underTest.generateManifestsZip(rootInputModule, scriptDir(), configuration); + underTest.generateManifestsArchive(rootInputModule, scriptDir(), configuration); var expectedArguments = List.of( "projects", "save-lockfiles", - "--zip", - "--zip-filename", - rootInputModule.getWorkDir().resolve("dependency-files.zip").toString(), + "--xz", + "--xz-filename", + rootInputModule.getWorkDir().resolve("dependency-files.tar.xz").toString(), "--directory", rootInputModule.getBaseDir().toString(), + "--recursive", "--exclude", "ignored.txt,.scannerwork/**", "--debug"); @@ -159,32 +161,30 @@ class CliServiceTest { } @Test - void generateZip_whenScaDebugEnabled_shouldWriteDebugLogsToInfoStream() throws IOException, URISyntaxException { - when(configuration.getBoolean("sonar.sca.debug")).thenReturn(Optional.of(true)); - + void generateManifestsArchive_whenScaDebugEnabled_shouldWriteDebugLogsToInfoStream() throws IOException, URISyntaxException { assertThat(rootModuleDir.resolve("test_file").toFile().createNewFile()).isTrue(); - underTest.generateManifestsZip(rootInputModule, scriptDir(), configuration); + underTest.generateManifestsArchive(rootInputModule, scriptDir(), configuration); var expectedArguments = List.of( "projects", "save-lockfiles", - "--zip", - "--zip-filename", - rootInputModule.getWorkDir().resolve("dependency-files.zip").toString(), + "--xz", + "--xz-filename", + rootInputModule.getWorkDir().resolve("dependency-files.tar.xz").toString(), "--directory", rootInputModule.getBaseDir().toString(), + "--recursive", "--exclude", - "ignored.txt,.scannerwork/**", - "--debug"); + "ignored.txt,.scannerwork/**"); assertThat(logTester.logs(INFO)) .contains("Arguments Passed In: " + String.join(" ", expectedArguments)); } @Test - void generateZip_shouldSendSQEnvVars() throws IOException, URISyntaxException { - underTest.generateManifestsZip(rootInputModule, scriptDir(), configuration); + void generateManifestsArchive_shouldSendSQEnvVars() throws IOException, URISyntaxException { + underTest.generateManifestsArchive(rootInputModule, scriptDir(), configuration); assertThat(logTester.logs(INFO)) .contains("TIDELIFT_CLI_INSIDE_SCANNER_ENGINE=1") @@ -192,20 +192,20 @@ class CliServiceTest { } @Test - void generateZip_includesIgnoredPathsFromGitProvider() throws Exception { - underTest.generateManifestsZip(rootInputModule, scriptDir(), configuration); + void generateManifestsArchive_includesIgnoredPathsFromGitProvider() throws Exception { + underTest.generateManifestsArchive(rootInputModule, scriptDir(), configuration); var expectedArguments = List.of( "projects", "save-lockfiles", - "--zip", - "--zip-filename", - rootInputModule.getWorkDir().resolve("dependency-files.zip").toString(), + "--xz", + "--xz-filename", + rootInputModule.getWorkDir().resolve("dependency-files.tar.xz").toString(), "--directory", rootInputModule.getBaseDir().toString(), + "--recursive", "--exclude", - "ignored.txt,.scannerwork/**", - "--debug"); + "ignored.txt,.scannerwork/**"); assertThat(logTester.logs(INFO)) .contains("Arguments Passed In: " + String.join(" ", expectedArguments)) @@ -217,87 +217,120 @@ class CliServiceTest { } @Test - void generateZip_withNoScm_doesNotIncludeScmIgnoredPaths() throws Exception { + void generateManifestsArchive_withNoScm_doesNotIncludeScmIgnoredPaths() throws Exception { when(scmConfiguration.provider()).thenReturn(null); - underTest.generateManifestsZip(rootInputModule, scriptDir(), configuration); + underTest.generateManifestsArchive(rootInputModule, scriptDir(), configuration); String capturedArgs = logTester.logs().stream().filter(log -> log.contains("Arguments Passed In:")).findFirst().get(); - assertThat(capturedArgs).contains("--exclude .scannerwork/** --debug"); + assertThat(capturedArgs).contains("--exclude .scannerwork/**"); } @Test - void generateZip_withNonGit_doesNotIncludeScmIgnoredPaths() throws Exception { + void generateManifestsArchive_withNonGit_doesNotIncludeScmIgnoredPaths() throws Exception { when(scmProvider.key()).thenReturn("notgit"); - underTest.generateManifestsZip(rootInputModule, scriptDir(), configuration); + underTest.generateManifestsArchive(rootInputModule, scriptDir(), configuration); String capturedArgs = logTester.logs().stream().filter(log -> log.contains("Arguments Passed In:")).findFirst().get(); - assertThat(capturedArgs).contains("--exclude .scannerwork/** --debug"); + assertThat(capturedArgs).contains("--exclude .scannerwork/**"); } @Test - void generateZip_withExclusionDisabled_doesNotIncludeScmIgnoredPaths() throws Exception { + void generateManifestsArchive_withScmExclusionDisabled_doesNotIncludeScmIgnoredPaths() throws Exception { when(scmConfiguration.isExclusionDisabled()).thenReturn(true); - underTest.generateManifestsZip(rootInputModule, scriptDir(), configuration); + underTest.generateManifestsArchive(rootInputModule, scriptDir(), configuration); String capturedArgs = logTester.logs().stream().filter(log -> log.contains("Arguments Passed In:")).findFirst().get(); - assertThat(capturedArgs).contains("--exclude .scannerwork/** --debug"); + assertThat(capturedArgs).contains("--exclude .scannerwork/**"); } @Test - void generateZip_withNoScmIgnores_doesNotIncludeScmIgnoredPaths() throws Exception { + void generateManifestsArchive_withNoScmIgnores_doesNotIncludeScmIgnoredPaths() throws Exception { jGitUtilsMock.when(() -> JGitUtils.getAllIgnoredPaths(any(Path.class))).thenReturn(List.of()); - underTest.generateManifestsZip(rootInputModule, scriptDir(), configuration); + underTest.generateManifestsArchive(rootInputModule, scriptDir(), configuration); String capturedArgs = logTester.logs().stream().filter(log -> log.contains("Arguments Passed In:")).findFirst().get(); - assertThat(capturedArgs).contains("--exclude .scannerwork/** --debug"); + assertThat(capturedArgs).contains("--exclude .scannerwork/**"); } @Test - void generateZip_withExistingExcludedManifests_appendsScmIgnoredPaths() throws Exception { - when(configuration.getStringArray(CliService.EXCLUDED_MANIFESTS_PROP_KEY)).thenReturn(new String[] {"**/test/**"}); + void generateManifestsArchive_withExcludedManifests_appendsScmIgnoredPaths() throws Exception { + when(configuration.getStringArray(CliService.SCA_EXCLUSIONS_KEY)).thenReturn(new String[] {"**/test/**"}); - underTest.generateManifestsZip(rootInputModule, scriptDir(), configuration); + underTest.generateManifestsArchive(rootInputModule, scriptDir(), configuration); String capturedArgs = logTester.logs().stream().filter(log -> log.contains("Arguments Passed In:")).findFirst().get(); assertThat(capturedArgs).contains("--exclude **/test/**,ignored.txt,.scannerwork/**"); } @Test - void generateZip_withExcludedManifestsSettingContainingBadCharacters_handlesTheBadCharacters() throws Exception { - when(configuration.getStringArray(CliService.EXCLUDED_MANIFESTS_PROP_KEY)).thenReturn(new String[] { - "**/test/**", "**/path with spaces/**", "**/path,with,commas/**", "**/path'with'quotes/**", "**/path\"with\"double\"quotes/**"}); + void generateManifestsArchive_withExcludedManifestsContainingBadCharacters_handlesTheBadCharacters() throws Exception { + when(configuration.getStringArray(CliService.SCA_EXCLUSIONS_KEY)).thenReturn(new String[] { + "**/test/**", "**/path with spaces/**", "**/path'with'quotes/**", "**/path\"with\"double\"quotes/**"}); - underTest.generateManifestsZip(rootInputModule, scriptDir(), configuration); + underTest.generateManifestsArchive(rootInputModule, scriptDir(), configuration); String capturedArgs = logTester.logs().stream().filter(log -> log.contains("Arguments Passed In:")).findFirst().get(); String expectedExcludeFlag = """ - --exclude **/test/**,**/path with spaces/**,"**/path,with,commas/**",**/path'with'quotes/**,"**/path""with""double""quotes/**",ignored.txt + --exclude **/test/**,**/path with spaces/**,**/path'with'quotes/**,"**/path""with""double""quotes/**",ignored.txt """.strip(); + if (SystemUtils.IS_OS_WINDOWS) { + expectedExcludeFlag = """ + --exclude "**/test/**,**/path with spaces/**,**/path'with'quotes/**,"**/path""with""double""quotes/**",ignored.txt + """.strip(); + } assertThat(capturedArgs).contains(expectedExcludeFlag); } @Test - void generateZip_withScmIgnoresContainingBadCharacters_handlesTheBadCharacters() throws Exception { + void generateManifestsArchive_withExcludedManifestsContainingDupes_dedupes() throws Exception { + when(configuration.getStringArray(CliService.SCA_EXCLUSIONS_KEY)).thenReturn(new String[] {"**/test1/**", "**/test2/**", "**/test1/**"}); + when(configuration.getStringArray(CliService.LEGACY_SCA_EXCLUSIONS_KEY)).thenReturn(new String[] {"**/test1/**", "**/test3/**"}); + + underTest.generateManifestsArchive(rootInputModule, scriptDir(), configuration); + + String capturedArgs = logTester.logs().stream().filter(log -> log.contains("Arguments Passed In:")).findFirst().get(); + assertThat(capturedArgs).contains("--exclude **/test1/**,**/test2/**,**/test3/**,ignored.txt,.scannerwork/**"); + } + + @Test + void generateManifestsArchive_withExcludedManifestsAndSonarExcludesContainingDupes_mergesAndDedupes() throws Exception { + when(projectExclusionFilters.getExclusionsConfig(InputFile.Type.MAIN)).thenReturn(new String[] {"**/test1/**", "**/test4/**"}); + when(configuration.getStringArray(CliService.SCA_EXCLUSIONS_KEY)).thenReturn(new String[] {"**/test1/**", "**/test2/**", "**/test1/**"}); + when(configuration.getStringArray(CliService.LEGACY_SCA_EXCLUSIONS_KEY)).thenReturn(new String[] {"**/test1/**", "**/test3/**"}); + + underTest.generateManifestsArchive(rootInputModule, scriptDir(), configuration); + + String capturedArgs = logTester.logs().stream().filter(log -> log.contains("Arguments Passed In:")).findFirst().get(); + assertThat(capturedArgs).contains("--exclude **/test1/**,**/test4/**,**/test2/**,**/test3/**,ignored.txt,.scannerwork/**"); + } + + @Test + void generateManifestsArchive_withScmIgnoresContainingBadCharacters_handlesTheBadCharacters() throws Exception { jGitUtilsMock.when(() -> JGitUtils.getAllIgnoredPaths(any(Path.class))) - .thenReturn(List.of("**/test/**", "**/path with spaces/**", "**/path,with,commas/**", "**/path'with'quotes/**", "**/path\"with\"double\"quotes/**")); + .thenReturn(List.of("**/test/**", "**/path with spaces/**", "**/path'with'quotes/**", "**/path\"with\"double\"quotes/**")); - underTest.generateManifestsZip(rootInputModule, scriptDir(), configuration); + underTest.generateManifestsArchive(rootInputModule, scriptDir(), configuration); String capturedArgs = logTester.logs().stream().filter(log -> log.contains("Arguments Passed In:")).findFirst().get(); String expectedExcludeFlag = """ - --exclude **/test/**,**/path with spaces/**,"**/path,with,commas/**",**/path'with'quotes/**,"**/path""with""double""quotes/**" + --exclude **/test/**,**/path with spaces/**,**/path'with'quotes/**,"**/path""with""double""quotes/**" """.strip(); + if (SystemUtils.IS_OS_WINDOWS) { + expectedExcludeFlag = """ + --exclude "**/test/**,**/path with spaces/**,**/path'with'quotes/**,"**/path""with""double""quotes/**" + """.strip(); + } assertThat(capturedArgs).contains(expectedExcludeFlag); } @Test - void generateZip_withIgnoredDirectories_GlobifiesDirectories() throws Exception { + void generateManifestsArchive_withIgnoredDirectories_GlobifiesDirectories() throws Exception { String ignoredDirectory = "directory1"; Files.createDirectories(rootModuleDir.resolve(ignoredDirectory)); String ignoredFile = "directory2/file.txt"; @@ -306,22 +339,22 @@ class CliServiceTest { Files.createFile(ignoredFilePath); jGitUtilsMock.when(() -> JGitUtils.getAllIgnoredPaths(any(Path.class))).thenReturn(List.of(ignoredDirectory, ignoredFile)); - underTest.generateManifestsZip(rootInputModule, scriptDir(), configuration); + underTest.generateManifestsArchive(rootInputModule, scriptDir(), configuration); String capturedArgs = logTester.logs().stream().filter(log -> log.contains("Arguments Passed In:")).findFirst().get(); assertThat(capturedArgs).contains("--exclude directory1/**,directory2/file.txt"); } @Test - void generateZip_withExternalWorkDir_DoesNotExcludeWorkingDir() throws URISyntaxException, IOException { + void generateManifestsArchive_withExternalWorkDir_DoesNotExcludeWorkingDir() throws URISyntaxException, IOException { Path externalWorkDir = Files.createTempDirectory("externalWorkDir"); try { rootInputModule = new DefaultInputModule(ProjectDefinition.create().setBaseDir(rootModuleDir.toFile()).setWorkDir(externalWorkDir.toFile())); - underTest.generateManifestsZip(rootInputModule, scriptDir(), configuration); + underTest.generateManifestsArchive(rootInputModule, scriptDir(), configuration); String capturedArgs = logTester.logs().stream().filter(log -> log.contains("Arguments Passed In:")).findFirst().get(); // externalWorkDir is not present in the exclude flag - assertThat(capturedArgs).contains("--exclude ignored.txt --debug"); + assertThat(capturedArgs).contains("--exclude ignored.txt"); } finally { externalWorkDir.toFile().delete(); } @@ -330,7 +363,7 @@ class CliServiceTest { private URL scriptUrl() { // There is a custom test Bash script available in src/test/resources/org/sonar/scanner/sca that // will serve as our "CLI". This script will output some messages about what arguments were passed - // to it and will try to generate a zip file in the location the process specifies. This allows us + // to it and will try to generate an archive file in the location the process specifies. This allows us // to simulate a real CLI call without needing an OS specific CLI executable to run on a real project. URL scriptUrl = CliServiceTest.class.getResource(SystemUtils.IS_OS_WINDOWS ? "echo_args.bat" : "echo_args.sh"); assertThat(scriptUrl).isNotNull(); diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/sca/ScaExecutorTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/sca/ScaExecutorTest.java index aecbc7011a9..ebe6007a1c1 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/sca/ScaExecutorTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/sca/ScaExecutorTest.java @@ -70,14 +70,14 @@ class ScaExecutorTest { File mockManifestZip = Files.newTemporaryFile(); ScannerReportWriter mockReportWriter = mock(ScannerReportWriter.class); when(cliCacheService.cacheCli()).thenReturn(mockCliFile); - when(cliService.generateManifestsZip(root, mockCliFile, configuration)).thenReturn(mockManifestZip); + when(cliService.generateManifestsArchive(root, mockCliFile, configuration)).thenReturn(mockManifestZip); when(reportPublisher.getWriter()).thenReturn(mockReportWriter); logTester.setLevel(Level.DEBUG); underTest.execute(root); - verify(cliService).generateManifestsZip(root, mockCliFile, configuration); + verify(cliService).generateManifestsArchive(root, mockCliFile, configuration); verify(mockReportWriter).writeScaFile(mockManifestZip); assertThat(logTester.logs(Level.DEBUG)).contains("Zip ready for report: " + mockManifestZip); assertThat(logTester.logs(Level.DEBUG)).contains("Manifest zip written to report"); @@ -87,13 +87,13 @@ class ScaExecutorTest { void execute_whenIOException_shouldHandleException() throws IOException { File mockCliFile = Files.newTemporaryFile(); when(cliCacheService.cacheCli()).thenReturn(mockCliFile); - doThrow(IOException.class).when(cliService).generateManifestsZip(root, mockCliFile, configuration); + doThrow(IOException.class).when(cliService).generateManifestsArchive(root, mockCliFile, configuration); logTester.setLevel(Level.INFO); underTest.execute(root); - verify(cliService).generateManifestsZip(root, mockCliFile, configuration); + verify(cliService).generateManifestsArchive(root, mockCliFile, configuration); assertThat(logTester.logs(Level.ERROR)).contains("Error gathering manifests"); } @@ -101,13 +101,13 @@ class ScaExecutorTest { void execute_whenIllegalStateException_shouldHandleException() throws IOException { File mockCliFile = Files.newTemporaryFile(); when(cliCacheService.cacheCli()).thenReturn(mockCliFile); - doThrow(IllegalStateException.class).when(cliService).generateManifestsZip(root, mockCliFile, configuration); + doThrow(IllegalStateException.class).when(cliService).generateManifestsArchive(root, mockCliFile, configuration); logTester.setLevel(Level.INFO); underTest.execute(root); - verify(cliService).generateManifestsZip(root, mockCliFile, configuration); + verify(cliService).generateManifestsArchive(root, mockCliFile, configuration); assertThat(logTester.logs(Level.ERROR)).contains("Error gathering manifests"); } @@ -118,7 +118,7 @@ class ScaExecutorTest { underTest.execute(root); - verify(cliService, never()).generateManifestsZip(root, mockCliFile, configuration); + verify(cliService, never()).generateManifestsArchive(root, mockCliFile, configuration); } @Test @@ -150,15 +150,31 @@ class ScaExecutorTest { File mockManifestZip = Files.newTemporaryFile(); ScannerReportWriter mockReportWriter = mock(ScannerReportWriter.class); when(cliCacheService.cacheCli()).thenReturn(mockCliFile); - when(cliService.generateManifestsZip(root, mockCliFile, configuration)).thenReturn(mockManifestZip); + when(cliService.generateManifestsArchive(root, mockCliFile, configuration)).thenReturn(mockManifestZip); when(reportPublisher.getWriter()).thenReturn(mockReportWriter); logTester.setLevel(Level.DEBUG); underTest.execute(root); - verify(cliService).generateManifestsZip(root, mockCliFile, configuration); + verify(cliService).generateManifestsArchive(root, mockCliFile, configuration); verify(mockReportWriter).writeScaFile(mockManifestZip); assertThat(logTester.logs(Level.DEBUG)).contains("Zip ready for report: " + mockManifestZip); assertThat(logTester.logs(Level.DEBUG)).contains("Manifest zip written to report"); } + + @Test + void execute_printsRuntime() throws IOException { + File mockCliFile = Files.newTemporaryFile(); + File mockManifestZip = Files.newTemporaryFile(); + ScannerReportWriter mockReportWriter = mock(ScannerReportWriter.class); + when(cliCacheService.cacheCli()).thenReturn(mockCliFile); + when(cliService.generateManifestsArchive(root, mockCliFile, configuration)).thenReturn(mockManifestZip); + when(reportPublisher.getWriter()).thenReturn(mockReportWriter); + + logTester.setLevel(Level.INFO); + + underTest.execute(root); + + assertThat(logTester.logs(Level.INFO)).anyMatch(l -> l.matches("Load SCA project dependencies \\(done\\) \\| time=\\d+ms")); + } } diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/sca/ScaPropertiesTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/sca/ScaPropertiesTest.java index e598a225b9c..70e7a6b6e53 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/sca/ScaPropertiesTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/sca/ScaPropertiesTest.java @@ -19,11 +19,9 @@ */ package org.sonar.scanner.sca; -import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Optional; -import java.util.Set; import org.junit.jupiter.api.Test; import org.sonar.scanner.config.DefaultConfiguration; @@ -36,13 +34,12 @@ class ScaPropertiesTest { private final DefaultConfiguration configuration = mock(DefaultConfiguration.class); @Test - void buildFromScannerProperties_withNoProperties_returnsDefaultMap() { + void buildFromScannerProperties_withNoProperties_returnsEmptyMap() { when(configuration.get(anyString())).thenReturn(Optional.empty()); - var result = ScaProperties.buildFromScannerProperties(configuration, Collections.emptySet()); + var result = ScaProperties.buildFromScannerProperties(configuration); - assertThat(result).containsExactly( - Map.entry("TIDELIFT_RECURSIVE_MANIFEST_SEARCH", "true")); + assertThat(result).isEqualTo(Map.of()); } @Test @@ -51,14 +48,13 @@ class ScaPropertiesTest { inputProperties.put("sonar.sca.pythonBinary", "/usr/bin/python3"); inputProperties.put("sonar.sca.unknownProperty", "value"); inputProperties.put("sonar.somethingElse", "dont-include-non-sca"); - inputProperties.put("sonar.sca.ignoredProperty", "ignore-me"); + inputProperties.put("sonar.sca.recursiveManifestSearch", "ignore-me"); when(configuration.getProperties()).thenReturn(inputProperties); when(configuration.get(anyString())).thenAnswer(i -> Optional.ofNullable(inputProperties.get(i.getArgument(0, String.class)))); - var result = ScaProperties.buildFromScannerProperties(configuration, Set.of("sonar.sca.ignoredProperty")); + var result = ScaProperties.buildFromScannerProperties(configuration); assertThat(result).containsExactly( - Map.entry("TIDELIFT_RECURSIVE_MANIFEST_SEARCH", "true"), Map.entry("TIDELIFT_PYTHON_BINARY", "/usr/bin/python3"), Map.entry("TIDELIFT_UNKNOWN_PROPERTY", "value")); } @@ -79,7 +75,6 @@ class ScaPropertiesTest { inputProperties.put("sonar.sca.pythonBinary", "/usr/bin/python3"); inputProperties.put("sonar.sca.pythonNoResolve", "true"); inputProperties.put("sonar.sca.pythonResolveLocal", "false"); - inputProperties.put("sonar.sca.recursiveManifestSearch", "true"); when(configuration.getProperties()).thenReturn(inputProperties); when(configuration.get(anyString())).thenAnswer(i -> Optional.ofNullable(inputProperties.get(i.getArgument(0, String.class)))); @@ -97,37 +92,9 @@ class ScaPropertiesTest { expectedProperties.put("TIDELIFT_PYTHON_BINARY", "/usr/bin/python3"); expectedProperties.put("TIDELIFT_PYTHON_NO_RESOLVE", "true"); expectedProperties.put("TIDELIFT_PYTHON_RESOLVE_LOCAL", "false"); - expectedProperties.put("TIDELIFT_RECURSIVE_MANIFEST_SEARCH", "true"); - var result = ScaProperties.buildFromScannerProperties(configuration, Collections.emptySet()); + var result = ScaProperties.buildFromScannerProperties(configuration); assertThat(result).containsExactlyInAnyOrderEntriesOf(expectedProperties); } - - - @Test - void buildFromScannerProperties_withoutRecursiveModeProp_defaultsRecursiveModeTrue() { - var inputProperties = new HashMap<String, String>(); - when(configuration.getProperties()).thenReturn(inputProperties); - when(configuration.get(anyString())).thenAnswer(i -> Optional.ofNullable(inputProperties.get(i.getArgument(0, String.class)))); - - var result = ScaProperties.buildFromScannerProperties(configuration, Collections.emptySet()); - - assertThat(result).containsExactly( - Map.entry("TIDELIFT_RECURSIVE_MANIFEST_SEARCH", "true")); - } - - @Test - void buildFromScannerProperties_withRecursiveModeProp_usesPropAsOverride() { - var inputProperties = new HashMap<String, String>(); - inputProperties.put("sonar.sca.recursiveManifestSearch", "false"); - when(configuration.getProperties()).thenReturn(inputProperties); - when(configuration.get(anyString())).thenAnswer(i -> Optional.ofNullable(inputProperties.get(i.getArgument(0, String.class)))); - - var result = ScaProperties.buildFromScannerProperties(configuration, Collections.emptySet()); - - assertThat(result).containsExactly( - Map.entry("TIDELIFT_RECURSIVE_MANIFEST_SEARCH", "false")); - } - } diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/ProjectConfigurationProviderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/ProjectConfigurationProviderTest.java index 21dcf58b114..3e3066e76e2 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/ProjectConfigurationProviderTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/ProjectConfigurationProviderTest.java @@ -52,10 +52,9 @@ public class ProjectConfigurationProviderTest { private static final Map<String, String> PROJECT_SERVER_PROPERTIES = Map.of(NON_GLOBAL_KEY_PROPERTIES_1, NON_GLOBAL_VALUE_PROPERTIES_1); private static final Map<String, String> DEFAULT_PROJECT_PROPERTIES = Map.of(DEFAULT_KEY_PROPERTIES_1, DEFAULT_VALUE_1); - private static final Map<String, String> ALL_PROPERTIES_MAP = - Stream.of(GLOBAL_SERVER_PROPERTIES, PROJECT_SERVER_PROPERTIES, DEFAULT_PROJECT_PROPERTIES) - .flatMap(map -> map.entrySet().stream()) - .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + private static final Map<String, String> ALL_PROPERTIES_MAP = Stream.of(GLOBAL_SERVER_PROPERTIES, PROJECT_SERVER_PROPERTIES, DEFAULT_PROJECT_PROPERTIES) + .flatMap(map -> map.entrySet().stream()) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); private static final Map<String, String> PROPERTIES_AFTER_FILTERING = Map.of("aKey", "aValue"); @@ -66,8 +65,6 @@ public class ProjectConfigurationProviderTest { @Mock private GlobalConfiguration globalConfiguration; @Mock - private MutableProjectSettings mutableProjectSettings; - @Mock private DefaultInputProject defaultInputProject; @Mock private SonarGlobalPropertiesFilter sonarGlobalPropertiesFilter; @@ -75,7 +72,6 @@ public class ProjectConfigurationProviderTest { @InjectMocks private ProjectConfigurationProvider provider; - @Before public void init() { when(globalConfiguration.getDefinitions()).thenReturn(new PropertyDefinitions(System2.INSTANCE)); @@ -89,11 +85,11 @@ public class ProjectConfigurationProviderTest { when(sonarGlobalPropertiesFilter.enforceOnlyServerSideSonarGlobalPropertiesAreUsed(ALL_PROPERTIES_MAP, GLOBAL_SERVER_PROPERTIES)) .thenReturn(PROPERTIES_AFTER_FILTERING); - ProjectConfiguration provide = provider.provide(defaultInputProject, globalConfiguration, globalServerSettings, projectServerSettings, mutableProjectSettings); + ProjectConfiguration provide = provider.provide(defaultInputProject, globalConfiguration, globalServerSettings, projectServerSettings); verify(sonarGlobalPropertiesFilter).enforceOnlyServerSideSonarGlobalPropertiesAreUsed(ALL_PROPERTIES_MAP, GLOBAL_SERVER_PROPERTIES); assertThat(provide.getOriginalProperties()).containsExactlyEntriesOf(PROPERTIES_AFTER_FILTERING); } -}
\ No newline at end of file +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/filesystem/DirectoryFileVisitorTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/filesystem/DirectoryFileVisitorTest.java index 01cb7d1bccf..277fc0dc68a 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/filesystem/DirectoryFileVisitorTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/filesystem/DirectoryFileVisitorTest.java @@ -27,20 +27,26 @@ import java.nio.file.Files; import java.nio.file.LinkOption; import java.nio.file.Path; import java.nio.file.attribute.BasicFileAttributes; +import java.util.Optional; import org.apache.commons.lang3.SystemUtils; +import org.junit.Before; import org.junit.ClassRule; import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.fs.internal.DefaultInputModule; +import org.sonar.scanner.bootstrap.SonarUserHome; import org.sonar.scanner.fs.InputModuleHierarchy; +import org.sonar.scanner.scan.ModuleConfiguration; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertThrows; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; public class DirectoryFileVisitorTest { @@ -48,33 +54,57 @@ public class DirectoryFileVisitorTest { public static TemporaryFolder temp = new TemporaryFolder(); private final DefaultInputModule module = mock(); + private final ModuleConfiguration moduleConfiguration = mock(); private final ModuleExclusionFilters moduleExclusionFilters = mock(); private final InputModuleHierarchy inputModuleHierarchy = mock(); private final InputFile.Type type = mock(); + private final SonarUserHome sonarUserHome = mock(); + private HiddenFilesProjectData hiddenFilesProjectData; + + @Before + public void before() throws IOException { + Path sonarUserHomePath = temp.newFolder().toPath(); + when(sonarUserHome.getPath()).thenReturn(sonarUserHomePath); + File workDir = temp.newFolder(); + when(module.getWorkDir()).thenReturn(workDir.toPath()); + hiddenFilesProjectData = spy(new HiddenFilesProjectData(sonarUserHome)); + } @Test - public void visit_hidden_file() throws IOException { + public void should_not_visit_hidden_file() throws IOException { + when(moduleConfiguration.getBoolean("sonar.scanner.excludeHiddenFiles")).thenReturn(Optional.of(true)); DirectoryFileVisitor.FileVisitAction action = mock(DirectoryFileVisitor.FileVisitAction.class); - File hidden = temp.newFile(".hidden"); - if (SystemUtils.IS_OS_WINDOWS) { - Files.setAttribute(hidden.toPath(), "dos:hidden", true, LinkOption.NOFOLLOW_LINKS); - } - + File hidden = temp.newFile(".hiddenNotVisited"); + setAsHiddenOnWindows(hidden); - DirectoryFileVisitor underTest = new DirectoryFileVisitor(action, module, moduleExclusionFilters, inputModuleHierarchy, type); + DirectoryFileVisitor underTest = new DirectoryFileVisitor(action, module, moduleConfiguration, moduleExclusionFilters, inputModuleHierarchy, type, hiddenFilesProjectData); underTest.visitFile(hidden.toPath(), Files.readAttributes(hidden.toPath(), BasicFileAttributes.class)); verify(action, never()).execute(any(Path.class)); } @Test + public void should_visit_hidden_file() throws IOException { + when(moduleConfiguration.getBoolean("sonar.scanner.excludeHiddenFiles")).thenReturn(Optional.of(false)); + DirectoryFileVisitor.FileVisitAction action = mock(DirectoryFileVisitor.FileVisitAction.class); + + File hidden = temp.newFile(".hiddenVisited"); + setAsHiddenOnWindows(hidden); + + DirectoryFileVisitor underTest = new DirectoryFileVisitor(action, module, moduleConfiguration, moduleExclusionFilters, inputModuleHierarchy, type, hiddenFilesProjectData); + underTest.visitFile(hidden.toPath(), Files.readAttributes(hidden.toPath(), BasicFileAttributes.class)); + + verify(action).execute(any(Path.class)); + } + + @Test public void test_visit_file_failed_generic_io_exception() throws IOException { DirectoryFileVisitor.FileVisitAction action = mock(DirectoryFileVisitor.FileVisitAction.class); File file = temp.newFile("failed"); - DirectoryFileVisitor underTest = new DirectoryFileVisitor(action, module, moduleExclusionFilters, inputModuleHierarchy, type); + DirectoryFileVisitor underTest = new DirectoryFileVisitor(action, module, moduleConfiguration, moduleExclusionFilters, inputModuleHierarchy, type, hiddenFilesProjectData); assertThrows(IOException.class, () -> underTest.visitFileFailed(file.toPath(), new IOException())); } @@ -84,10 +114,15 @@ public class DirectoryFileVisitorTest { File file = temp.newFile("symlink"); - DirectoryFileVisitor underTest = new DirectoryFileVisitor(action, module, moduleExclusionFilters, inputModuleHierarchy, type); + DirectoryFileVisitor underTest = new DirectoryFileVisitor(action, module, moduleConfiguration, moduleExclusionFilters, inputModuleHierarchy, type, hiddenFilesProjectData); FileVisitResult result = underTest.visitFileFailed(file.toPath(), new FileSystemLoopException(file.getPath())); assertThat(result).isEqualTo(FileVisitResult.CONTINUE); } + private static void setAsHiddenOnWindows(File file) throws IOException { + if (SystemUtils.IS_OS_WINDOWS) { + Files.setAttribute(file.toPath(), "dos:hidden", true, LinkOption.NOFOLLOW_LINKS); + } + } } diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/filesystem/HiddenFilesProjectDataTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/filesystem/HiddenFilesProjectDataTest.java new file mode 100644 index 00000000000..d5a6e4ff843 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/filesystem/HiddenFilesProjectDataTest.java @@ -0,0 +1,160 @@ +/* + * SonarQube + * Copyright (C) 2009-2025 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.scan.filesystem; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.LinkOption; +import java.nio.file.Path; +import org.apache.commons.lang3.SystemUtils; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.sonar.api.batch.fs.internal.DefaultInputModule; +import org.sonar.scanner.bootstrap.SonarUserHome; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; + +public class HiddenFilesProjectDataTest { + + @ClassRule + public static TemporaryFolder temp = new TemporaryFolder(); + + private static final SonarUserHome sonarUserHome = mock(SonarUserHome.class); + private final DefaultInputModule inputModule = mock(DefaultInputModule.class); + private final DefaultInputModule secondInputModule = mock(DefaultInputModule.class); + private HiddenFilesProjectData underTest; + + @BeforeClass + public static void setUp() throws IOException { + File userHomeFolder = temp.newFolder(".userhome"); + setAsHiddenOnWindows(userHomeFolder); + when(sonarUserHome.getPath()).thenReturn(userHomeFolder.toPath()); + } + + @Before + public void before() { + underTest = spy(new HiddenFilesProjectData(sonarUserHome)); + } + + @Test + public void shouldContainNoMarkedHiddenFileOnConstruction() { + assertThat(underTest.hiddenFilesByModule).isEmpty(); + } + + @Test + public void shouldMarkWithCorrectAssociatedInputModule() { + Path myFile = Path.of("myFile"); + Path myFile2 = Path.of("myFile2"); + underTest.markAsHiddenFile(myFile, inputModule); + underTest.markAsHiddenFile(myFile2, inputModule); + + assertThat(underTest.hiddenFilesByModule).hasSize(1); + assertThat(underTest.getIsMarkedAsHiddenFileAndRemoveVisibilityInformation(myFile, inputModule)).isTrue(); + assertThat(underTest.getIsMarkedAsHiddenFileAndRemoveVisibilityInformation(myFile2, inputModule)).isTrue(); + assertThat(underTest.getIsMarkedAsHiddenFileAndRemoveVisibilityInformation(myFile, secondInputModule)).isFalse(); + assertThat(underTest.getIsMarkedAsHiddenFileAndRemoveVisibilityInformation(myFile2, secondInputModule)).isFalse(); + } + + @Test + public void shouldMarkWithCorrectAssociatedInputModuleForTwoDifferentModules() { + Path myFile = Path.of("myFile"); + Path myFile2 = Path.of("myFile2"); + underTest.markAsHiddenFile(myFile, inputModule); + underTest.markAsHiddenFile(myFile2, secondInputModule); + + assertThat(underTest.hiddenFilesByModule).hasSize(2); + assertThat(underTest.getIsMarkedAsHiddenFileAndRemoveVisibilityInformation(myFile, inputModule)).isTrue(); + assertThat(underTest.getIsMarkedAsHiddenFileAndRemoveVisibilityInformation(myFile2, inputModule)).isFalse(); + assertThat(underTest.getIsMarkedAsHiddenFileAndRemoveVisibilityInformation(myFile, secondInputModule)).isFalse(); + assertThat(underTest.getIsMarkedAsHiddenFileAndRemoveVisibilityInformation(myFile2, secondInputModule)).isTrue(); + } + + @Test + public void shouldNotShowAsHiddenFileWhenInputModuleIsNotExistingInData() { + Path myFile = Path.of("myFile"); + Path notMarkedFile = Path.of("notMarkedFile"); + underTest.markAsHiddenFile(myFile, inputModule); + + assertThat(underTest.hiddenFilesByModule).isNotEmpty(); + assertThat(underTest.getIsMarkedAsHiddenFileAndRemoveVisibilityInformation(notMarkedFile, secondInputModule)).isFalse(); + } + + @Test + public void shouldClearMap() { + Path myFile = Path.of("myFile"); + Path myFile2 = Path.of("myFile2"); + underTest.markAsHiddenFile(myFile, inputModule); + underTest.markAsHiddenFile(myFile2, secondInputModule); + + assertThat(underTest.hiddenFilesByModule).hasSize(2); + + underTest.clearHiddenFilesData(); + assertThat(underTest.hiddenFilesByModule).isEmpty(); + } + + @Test + public void shouldRemoveVisibilityAfterQuerying() { + Path myFile = Path.of("myFile"); + Path myFile2 = Path.of("myFile2"); + underTest.markAsHiddenFile(myFile, inputModule); + underTest.markAsHiddenFile(myFile2, inputModule); + + assertThat(underTest.hiddenFilesByModule).hasSize(1); + assertThat(underTest.getIsMarkedAsHiddenFileAndRemoveVisibilityInformation(myFile, inputModule)).isTrue(); + assertThat(underTest.getIsMarkedAsHiddenFileAndRemoveVisibilityInformation(myFile2, inputModule)).isTrue(); + + assertThat(underTest.hiddenFilesByModule).hasSize(1); + assertThat(underTest.hiddenFilesByModule.get(inputModule)).isEmpty(); + assertThat(underTest.getIsMarkedAsHiddenFileAndRemoveVisibilityInformation(myFile, inputModule)).isFalse(); + assertThat(underTest.getIsMarkedAsHiddenFileAndRemoveVisibilityInformation(myFile2, inputModule)).isFalse(); + } + + @Test + public void shouldOnlyRemoveModuleIfAllFilesAreRemoved() { + Path myFile = Path.of("myFile"); + Path myFile2 = Path.of("myFile2"); + underTest.markAsHiddenFile(myFile, inputModule); + underTest.markAsHiddenFile(myFile2, inputModule); + + assertThat(underTest.hiddenFilesByModule).hasSize(1); + assertThat(underTest.getIsMarkedAsHiddenFileAndRemoveVisibilityInformation(myFile, inputModule)).isTrue(); + + assertThat(underTest.hiddenFilesByModule).isNotEmpty(); + } + + @Test + public void shouldNotFailOnUserPathResolving() throws IOException { + Path expectedPath = sonarUserHome.getPath().toRealPath(LinkOption.NOFOLLOW_LINKS).toAbsolutePath().normalize(); + assertThat(underTest.getCachedSonarUserHomePath()).isEqualTo(expectedPath); + } + + private static void setAsHiddenOnWindows(File file) throws IOException { + if (SystemUtils.IS_OS_WINDOWS) { + Files.setAttribute(file.toPath(), "dos:hidden", true, LinkOption.NOFOLLOW_LINKS); + } + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/filesystem/HiddenFilesVisitorHelperTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/filesystem/HiddenFilesVisitorHelperTest.java new file mode 100644 index 00000000000..8c111c7ea15 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/filesystem/HiddenFilesVisitorHelperTest.java @@ -0,0 +1,315 @@ +/* + * SonarQube + * Copyright (C) 2009-2025 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.scan.filesystem; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.LinkOption; +import java.nio.file.Path; +import java.util.Optional; +import org.apache.commons.lang3.SystemUtils; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.sonar.api.batch.fs.internal.DefaultInputModule; +import org.sonar.scanner.bootstrap.SonarUserHome; +import org.sonar.scanner.scan.ModuleConfiguration; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class HiddenFilesVisitorHelperTest { + + @ClassRule + public static TemporaryFolder temp = new TemporaryFolder(); + + private static final SonarUserHome sonarUserHome = mock(SonarUserHome.class); + private static final DefaultInputModule inputModule = mock(DefaultInputModule.class); + + private final ModuleConfiguration moduleConfig = mock(ModuleConfiguration.class); + private final HiddenFilesProjectData hiddenFilesProjectData = spy(new HiddenFilesProjectData(sonarUserHome)); + private HiddenFilesVisitorHelper underTest; + + @BeforeClass + public static void setUp() throws IOException { + File userHomeFolder = temp.newFolder(".userhome"); + setAsHiddenOnWindows(userHomeFolder); + when(sonarUserHome.getPath()).thenReturn(userHomeFolder.toPath()); + + File workDir = temp.newFolder(".sonar"); + setAsHiddenOnWindows(workDir); + when(inputModule.getWorkDir()).thenReturn(workDir.toPath()); + } + + @Before + public void before() { + hiddenFilesProjectData.clearHiddenFilesData(); + underTest = spy(new HiddenFilesVisitorHelper(hiddenFilesProjectData, inputModule, moduleConfig)); + } + + @Test + public void verifyDefaultOnConstruction() { + assertThat(underTest.excludeHiddenFiles).isFalse(); + assertThat(underTest.rootHiddenDir).isNull(); + } + + @Test + public void excludeHiddenFilesShouldBeSetToFalseFromConfigurationWhenNotConfigured() { + when(moduleConfig.getBoolean("sonar.scanner.excludeHiddenFiles")).thenReturn(Optional.empty()); + HiddenFilesVisitorHelper configuredVisitorHelper = spy(new HiddenFilesVisitorHelper(hiddenFilesProjectData, inputModule, moduleConfig)); + + assertThat(configuredVisitorHelper.excludeHiddenFiles).isFalse(); + } + + @Test + public void excludeHiddenFilesShouldBeSetToFalseFromConfigurationWhenDisabled() { + when(moduleConfig.getBoolean("sonar.scanner.excludeHiddenFiles")).thenReturn(Optional.of(false)); + HiddenFilesVisitorHelper configuredVisitorHelper = spy(new HiddenFilesVisitorHelper(hiddenFilesProjectData, inputModule, moduleConfig)); + + assertThat(configuredVisitorHelper.excludeHiddenFiles).isFalse(); + } + + @Test + public void excludeHiddenFilesShouldBeSetToTrueFromConfigurationWhenEnabled() { + when(moduleConfig.getBoolean("sonar.scanner.excludeHiddenFiles")).thenReturn(Optional.of(true)); + HiddenFilesVisitorHelper configuredVisitorHelper = spy(new HiddenFilesVisitorHelper(hiddenFilesProjectData, inputModule, moduleConfig)); + + assertThat(configuredVisitorHelper.excludeHiddenFiles).isTrue(); + } + + @Test + public void shouldVisitHiddenDirectory() throws IOException { + File hiddenDir = temp.newFolder(".hiddenVisited"); + setAsHiddenOnWindows(hiddenDir); + + boolean visitDir = underTest.shouldVisitDir(hiddenDir.toPath()); + + assertThat(visitDir).isTrue(); + assertThat(underTest.insideHiddenDirectory()).isTrue(); + assertThat(underTest.rootHiddenDir).isEqualTo(hiddenDir.toPath()); + verify(underTest).enterHiddenDirectory(hiddenDir.toPath()); + } + + @Test + public void shouldNotVisitHiddenDirectoryWhenHiddenFilesVisitIsExcluded() throws IOException { + when(moduleConfig.getBoolean("sonar.scanner.excludeHiddenFiles")).thenReturn(Optional.of(true)); + HiddenFilesVisitorHelper configuredVisitorHelper = spy(new HiddenFilesVisitorHelper(hiddenFilesProjectData, inputModule, moduleConfig)); + + File hidden = temp.newFolder(".hiddenNotVisited"); + setAsHiddenOnWindows(hidden); + + boolean visitDir = configuredVisitorHelper.shouldVisitDir(hidden.toPath()); + + assertThat(visitDir).isFalse(); + assertThat(configuredVisitorHelper.insideHiddenDirectory()).isFalse(); + verify(configuredVisitorHelper, never()).enterHiddenDirectory(any()); + } + + @Test + public void shouldVisitNonHiddenDirectoryWhenHiddenFilesVisitIsExcluded() throws IOException { + when(moduleConfig.getBoolean("sonar.scanner.excludeHiddenFiles")).thenReturn(Optional.of(true)); + HiddenFilesVisitorHelper configuredVisitorHelper = spy(new HiddenFilesVisitorHelper(hiddenFilesProjectData, inputModule, moduleConfig)); + + File nonHiddenFolder = temp.newFolder(); + + boolean visitDir = configuredVisitorHelper.shouldVisitDir(nonHiddenFolder.toPath()); + + assertThat(visitDir).isTrue(); + assertThat(configuredVisitorHelper.insideHiddenDirectory()).isFalse(); + verify(configuredVisitorHelper, never()).enterHiddenDirectory(any()); + } + + @Test + public void shouldVisitNonHiddenDirectory() throws IOException { + File nonHiddenFolder = temp.newFolder(); + + boolean visitDir = underTest.shouldVisitDir(nonHiddenFolder.toPath()); + + assertThat(visitDir).isTrue(); + assertThat(underTest.insideHiddenDirectory()).isFalse(); + verify(underTest, never()).enterHiddenDirectory(any()); + assertThat(underTest.excludeHiddenFiles).isFalse(); + } + + @Test + public void shouldNotVisitModuleWorkDir() throws IOException { + Path workingDirectory = inputModule.getWorkDir().toRealPath(LinkOption.NOFOLLOW_LINKS).toAbsolutePath().normalize(); + boolean visitDir = underTest.shouldVisitDir(workingDirectory); + + assertThat(visitDir).isFalse(); + assertThat(underTest.insideHiddenDirectory()).isFalse(); + verify(underTest, never()).enterHiddenDirectory(any()); + } + + @Test + public void shouldNotVisitSonarUserHome() throws IOException { + Path userHome = sonarUserHome.getPath().toRealPath(LinkOption.NOFOLLOW_LINKS).toAbsolutePath().normalize(); + boolean visitDir = underTest.shouldVisitDir(userHome); + + assertThat(visitDir).isFalse(); + assertThat(underTest.insideHiddenDirectory()).isFalse(); + verify(underTest, never()).enterHiddenDirectory(any()); + } + + @Test + public void hiddenFileShouldBeVisited() throws IOException { + File hiddenFile = temp.newFile(".hiddenFileShouldBeVisited"); + setAsHiddenOnWindows(hiddenFile); + + assertThat(underTest.insideHiddenDirectory()).isFalse(); + boolean visitFile = underTest.shouldVisitFile(hiddenFile.toPath()); + + assertThat(visitFile).isTrue(); + verify(hiddenFilesProjectData).markAsHiddenFile(hiddenFile.toPath(), inputModule); + } + + @Test + public void nonHiddenFileShouldBeVisitedInHiddenFolder() throws IOException { + File hidden = temp.newFolder(".hiddenFolder"); + setAsHiddenOnWindows(hidden); + + File nonHiddenFile = temp.newFile(); + + underTest.shouldVisitDir(hidden.toPath()); + assertThat(underTest.insideHiddenDirectory()).isTrue(); + + boolean shouldVisitFile = underTest.shouldVisitFile(nonHiddenFile.toPath()); + + assertThat(shouldVisitFile).isTrue(); + verify(hiddenFilesProjectData).markAsHiddenFile(nonHiddenFile.toPath(), inputModule); + } + + @Test + public void shouldNotSetAsRootHiddenDirectoryWhenAlreadyEnteredHiddenDirectory() throws IOException { + File hidden = temp.newFolder(".outerHiddenFolder"); + File nestedHiddenFolder = temp.newFolder(".outerHiddenFolder", ".nestedHiddenFolder"); + setAsHiddenOnWindows(hidden); + setAsHiddenOnWindows(nestedHiddenFolder); + + underTest.shouldVisitDir(hidden.toPath()); + assertThat(underTest.insideHiddenDirectory()).isTrue(); + + boolean shouldVisitNestedDir = underTest.shouldVisitDir(nestedHiddenFolder.toPath()); + + assertThat(shouldVisitNestedDir).isTrue(); + assertThat(underTest.rootHiddenDir).isEqualTo(hidden.toPath()); + verify(underTest).enterHiddenDirectory(nestedHiddenFolder.toPath()); + } + + @Test + public void hiddenFileShouldNotBeVisitedWhenHiddenFileVisitExcluded() throws IOException { + when(moduleConfig.getBoolean("sonar.scanner.excludeHiddenFiles")).thenReturn(Optional.of(true)); + HiddenFilesVisitorHelper configuredVisitorHelper = spy(new HiddenFilesVisitorHelper(hiddenFilesProjectData, inputModule, moduleConfig)); + + File hiddenFile = temp.newFile(".hiddenFileNotVisited"); + setAsHiddenOnWindows(hiddenFile); + + assertThat(configuredVisitorHelper.insideHiddenDirectory()).isFalse(); + + configuredVisitorHelper.shouldVisitFile(hiddenFile.toPath()); + boolean shouldVisitFile = configuredVisitorHelper.shouldVisitFile(hiddenFile.toPath()); + + assertThat(shouldVisitFile).isFalse(); + verify(hiddenFilesProjectData, never()).markAsHiddenFile(hiddenFile.toPath(), inputModule); + } + + @Test + public void shouldCorrectlyExitHiddenFolderOnlyOnHiddenFolderThatEntered() throws IOException { + File hiddenFolder = temp.newFolder(".hiddenRootFolder"); + setAsHiddenOnWindows(hiddenFolder); + + boolean shouldVisitDir = underTest.shouldVisitDir(hiddenFolder.toPath()); + + assertThat(shouldVisitDir).isTrue(); + assertThat(underTest.insideHiddenDirectory()).isTrue(); + assertThat(underTest.rootHiddenDir).isEqualTo(hiddenFolder.toPath()); + verify(underTest).enterHiddenDirectory(hiddenFolder.toPath()); + + File folder1 = temp.newFolder(".hiddenRootFolder", "myFolderExit"); + File folder2 = temp.newFolder("myFolderExit"); + File folder3 = temp.newFolder(".myFolderExit"); + setAsHiddenOnWindows(folder3); + + underTest.exitDirectory(folder1.toPath()); + underTest.exitDirectory(folder2.toPath()); + underTest.exitDirectory(folder3.toPath()); + + assertThat(underTest.insideHiddenDirectory()).isTrue(); + assertThat(underTest.rootHiddenDir).isEqualTo(hiddenFolder.toPath()); + verify(underTest, never()).resetRootHiddenDir(); + + underTest.exitDirectory(hiddenFolder.toPath()); + assertThat(underTest.insideHiddenDirectory()).isFalse(); + assertThat(underTest.rootHiddenDir).isNull(); + verify(underTest).resetRootHiddenDir(); + } + + @Test + public void shouldNotInitiateResetRootDirWhenNotInHiddenDirectory() throws IOException { + File hiddenFolder = temp.newFolder(".hiddenFolderNonRoot"); + setAsHiddenOnWindows(hiddenFolder); + + underTest.exitDirectory(hiddenFolder.toPath()); + + verify(underTest, never()).resetRootHiddenDir(); + } + + @Test + public void filesShouldBeCorrectlyMarkedAsHidden() throws IOException { + File hiddenFolder = temp.newFolder(".hiddenFolderRoot"); + setAsHiddenOnWindows(hiddenFolder); + + File file1 = temp.newFile(); + File file2 = temp.newFile(); + File file3 = temp.newFile(".markedHiddenFile"); + setAsHiddenOnWindows(file3); + File file4 = temp.newFile(); + File file5 = temp.newFile(".markedHiddenFile2"); + setAsHiddenOnWindows(file5); + + underTest.shouldVisitFile(file1.toPath()); + underTest.shouldVisitDir(hiddenFolder.toPath()); + underTest.shouldVisitFile(file2.toPath()); + underTest.shouldVisitFile(file3.toPath()); + underTest.exitDirectory(hiddenFolder.toPath()); + underTest.shouldVisitFile(file4.toPath()); + underTest.shouldVisitFile(file5.toPath()); + + verify(hiddenFilesProjectData, never()).markAsHiddenFile(file1.toPath(), inputModule); + verify(hiddenFilesProjectData).markAsHiddenFile(file2.toPath(), inputModule); + verify(hiddenFilesProjectData).markAsHiddenFile(file3.toPath(), inputModule); + verify(hiddenFilesProjectData, never()).markAsHiddenFile(file4.toPath(), inputModule); + verify(hiddenFilesProjectData).markAsHiddenFile(file5.toPath(), inputModule); + } + + private static void setAsHiddenOnWindows(File file) throws IOException { + if (SystemUtils.IS_OS_WINDOWS) { + Files.setAttribute(file.toPath(), "dos:hidden", true, LinkOption.NOFOLLOW_LINKS); + } + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/filesystem/ModuleInputComponentStoreTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/filesystem/ModuleInputComponentStoreTest.java index a0031f77633..07f7afec036 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/filesystem/ModuleInputComponentStoreTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/filesystem/ModuleInputComponentStoreTest.java @@ -19,80 +19,151 @@ */ package org.sonar.scanner.scan.filesystem; -import java.io.IOException; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; +import java.io.File; +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.io.TempDir; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; import org.sonar.api.SonarRuntime; import org.sonar.api.batch.fs.InputFile; -import org.sonar.api.batch.fs.InputModule; import org.sonar.api.batch.fs.internal.SensorStrategy; -import org.sonar.api.batch.fs.internal.DefaultInputProject; import org.sonar.api.batch.fs.internal.TestInputFileBuilder; +import org.sonar.api.batch.sensor.internal.SensorContextTester; import org.sonar.scanner.scan.branch.BranchConfiguration; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -public class ModuleInputComponentStoreTest { - @Rule - public TemporaryFolder temp = new TemporaryFolder(); +@ExtendWith(MockitoExtension.class) +class ModuleInputComponentStoreTest { + + @TempDir + private File projectBaseDir; + + @Mock + BranchConfiguration branchConfiguration; + + @Mock + SonarRuntime sonarRuntime; + + @Mock + InputComponentStore mockedInputComponentStore; private InputComponentStore componentStore; + private SensorContextTester sensorContextTester; private final String projectKey = "dummy key"; - @Before - public void setUp() throws IOException { - DefaultInputProject root = TestInputFileBuilder.newDefaultInputProject(projectKey, temp.newFolder()); - componentStore = new InputComponentStore(mock(BranchConfiguration.class), mock(SonarRuntime.class)); + @BeforeEach + void setUp() { + TestInputFileBuilder.newDefaultInputProject(projectKey, projectBaseDir); + File moduleBaseDir = new File(projectBaseDir, "module"); + moduleBaseDir.mkdir(); + sensorContextTester = SensorContextTester.create(moduleBaseDir); + componentStore = spy(new InputComponentStore(branchConfiguration, sonarRuntime)); } @Test - public void should_cache_files_by_filename() { + void should_cache_module_files_by_filename() { ModuleInputComponentStore store = newModuleInputComponentStore(); String filename = "some name"; - InputFile inputFile1 = new TestInputFileBuilder(projectKey, "some/path/" + filename).build(); + InputFile inputFile1 = new TestInputFileBuilder(projectKey, "module/some/path/" + filename).build(); store.doAdd(inputFile1); - InputFile inputFile2 = new TestInputFileBuilder(projectKey, "other/path/" + filename).build(); + InputFile inputFile2 = new TestInputFileBuilder(projectKey, "module/other/path/" + filename).build(); store.doAdd(inputFile2); - InputFile dummyInputFile = new TestInputFileBuilder(projectKey, "some/path/Dummy.java").build(); + InputFile dummyInputFile = new TestInputFileBuilder(projectKey, "module/some/path/Dummy.java").build(); store.doAdd(dummyInputFile); assertThat(store.getFilesByName(filename)).containsExactlyInAnyOrder(inputFile1, inputFile2); } @Test - public void should_cache_files_by_extension() { + void should_cache_filtered_module_files_by_filename() { + ModuleInputComponentStore store = newModuleInputComponentStore(); + + String filename = "some name"; + InputFile inputFile1 = new TestInputFileBuilder(projectKey, "some/path/" + filename).build(); + InputFile inputFile2 = new TestInputFileBuilder(projectKey, "module/other/path/" + filename).build(); + store.doAdd(inputFile2); + + when(componentStore.getFilesByName(filename)).thenReturn(List.of(inputFile1, inputFile2)); + + assertThat(store.getFilesByName(filename)).containsOnly(inputFile2); + } + + @Test + void should_cache_module_files_by_filename_global_strategy() { + ModuleInputComponentStore store = new ModuleInputComponentStore(sensorContextTester.module(), componentStore, new SensorStrategy()); + + String filename = "some name"; + // None in the module + InputFile inputFile1 = new TestInputFileBuilder(projectKey, "some/path/" + filename).build(); + InputFile inputFile2 = new TestInputFileBuilder(projectKey, "other/path/" + filename).build(); + + when(componentStore.getFilesByName(filename)).thenReturn(List.of(inputFile1, inputFile2)); + + assertThat(store.getFilesByName(filename)).containsExactlyInAnyOrder(inputFile1, inputFile2); + } + + @Test + void should_cache_module_files_by_extension() { ModuleInputComponentStore store = newModuleInputComponentStore(); - InputFile inputFile1 = new TestInputFileBuilder(projectKey, "some/path/Program.java").build(); + InputFile inputFile1 = new TestInputFileBuilder(projectKey, "module/some/path/Program.java").build(); store.doAdd(inputFile1); - InputFile inputFile2 = new TestInputFileBuilder(projectKey, "other/path/Utils.java").build(); + InputFile inputFile2 = new TestInputFileBuilder(projectKey, "module/other/path/Utils.java").build(); store.doAdd(inputFile2); - InputFile dummyInputFile = new TestInputFileBuilder(projectKey, "some/path/NotJava.cpp").build(); + InputFile dummyInputFile = new TestInputFileBuilder(projectKey, "module/some/path/NotJava.cpp").build(); store.doAdd(dummyInputFile); assertThat(store.getFilesByExtension("java")).containsExactlyInAnyOrder(inputFile1, inputFile2); } @Test - public void should_not_cache_duplicates() { + void should_cache_filtered_module_files_by_extension() { + ModuleInputComponentStore store = newModuleInputComponentStore(); + + InputFile inputFile1 = new TestInputFileBuilder(projectKey, "some/path/NotInModule.java").build(); + InputFile inputFile2 = new TestInputFileBuilder(projectKey, "module/some/path/Other.java").build(); + store.doAdd(inputFile2); + + when(componentStore.getFilesByExtension("java")).thenReturn(List.of(inputFile1, inputFile2)); + + assertThat(store.getFilesByExtension("java")).containsOnly(inputFile2); + } + + @Test + void should_cache_module_files_by_extension_global_strategy() { + ModuleInputComponentStore store = new ModuleInputComponentStore(sensorContextTester.module(), componentStore, new SensorStrategy()); + + // None in the module + InputFile inputFile1 = new TestInputFileBuilder(projectKey, "some/path/NotInModule.java").build(); + InputFile inputFile2 = new TestInputFileBuilder(projectKey, "some/path/Other.java").build(); + + when(componentStore.getFilesByExtension("java")).thenReturn(List.of(inputFile1, inputFile2)); + + assertThat(store.getFilesByExtension("java")).containsExactlyInAnyOrder(inputFile1, inputFile2); + } + + @Test + void should_not_cache_duplicates() { ModuleInputComponentStore store = newModuleInputComponentStore(); String ext = "java"; String filename = "Program." + ext; - InputFile inputFile = new TestInputFileBuilder(projectKey, "some/path/" + filename).build(); + InputFile inputFile = new TestInputFileBuilder(projectKey, "module/some/path/" + filename).build(); store.doAdd(inputFile); store.doAdd(inputFile); store.doAdd(inputFile); @@ -102,12 +173,12 @@ public class ModuleInputComponentStoreTest { } @Test - public void should_get_empty_iterable_on_cache_miss() { + void should_get_empty_iterable_on_cache_miss() { ModuleInputComponentStore store = newModuleInputComponentStore(); String ext = "java"; String filename = "Program." + ext; - InputFile inputFile = new TestInputFileBuilder(projectKey, "some/path/" + filename).build(); + InputFile inputFile = new TestInputFileBuilder(projectKey, "module/some/path/" + filename).build(); store.doAdd(inputFile); assertThat(store.getFilesByName("nonexistent")).isEmpty(); @@ -115,48 +186,42 @@ public class ModuleInputComponentStoreTest { } private ModuleInputComponentStore newModuleInputComponentStore() { - InputModule module = mock(InputModule.class); - when(module.key()).thenReturn("moduleKey"); - return new ModuleInputComponentStore(module, componentStore, mock(SensorStrategy.class)); + SensorStrategy strategy = new SensorStrategy(); + strategy.setGlobal(false); + return new ModuleInputComponentStore(sensorContextTester.module(), componentStore, strategy); } @Test - public void should_find_module_components_with_non_global_strategy() { - InputComponentStore inputComponentStore = mock(InputComponentStore.class); + void should_find_module_components_with_non_global_strategy() { SensorStrategy strategy = new SensorStrategy(); - InputModule module = mock(InputModule.class); - when(module.key()).thenReturn("foo"); - ModuleInputComponentStore store = new ModuleInputComponentStore(module, inputComponentStore, strategy); + ModuleInputComponentStore store = new ModuleInputComponentStore(sensorContextTester.module(), mockedInputComponentStore, strategy); strategy.setGlobal(false); store.inputFiles(); - verify(inputComponentStore).filesByModule("foo"); + verify(mockedInputComponentStore).filesByModule(sensorContextTester.module().key()); String relativePath = "somepath"; store.inputFile(relativePath); - verify(inputComponentStore).getFile(any(String.class), eq(relativePath)); + verify(mockedInputComponentStore).getFile(any(String.class), eq(relativePath)); store.languages(); - verify(inputComponentStore).languages(any(String.class)); + verify(mockedInputComponentStore).languages(any(String.class)); } @Test - public void should_find_all_components_with_global_strategy() { - InputComponentStore inputComponentStore = mock(InputComponentStore.class); + void should_find_all_components_with_global_strategy() { SensorStrategy strategy = new SensorStrategy(); - ModuleInputComponentStore store = new ModuleInputComponentStore(mock(InputModule.class), inputComponentStore, strategy); - - strategy.setGlobal(true); + ModuleInputComponentStore store = new ModuleInputComponentStore(sensorContextTester.module(), mockedInputComponentStore, strategy); store.inputFiles(); - verify(inputComponentStore).inputFiles(); + verify(mockedInputComponentStore).inputFiles(); String relativePath = "somepath"; store.inputFile(relativePath); - verify(inputComponentStore).inputFile(relativePath); + verify(mockedInputComponentStore).inputFile(relativePath); store.languages(); - verify(inputComponentStore).languages(); + verify(mockedInputComponentStore).languages(); } } diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/filesystem/MutableFileSystemTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/filesystem/MutableFileSystemTest.java index 31d3312853b..485708c9936 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/filesystem/MutableFileSystemTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/filesystem/MutableFileSystemTest.java @@ -28,6 +28,9 @@ import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.fs.internal.TestInputFileBuilder; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; public class MutableFileSystemTest { @@ -44,9 +47,15 @@ public class MutableFileSystemTest { } @Test - public void return_all_files_when_not_restricted() { + public void restriction_and_hidden_file_should_be_disabled_on_default() { + assertThat(underTest.restrictToChangedFiles).isFalse(); + assertThat(underTest.allowHiddenFileAnalysis).isFalse(); + } + + @Test + public void return_all_non_hidden_files_when_not_restricted_and_disabled() { assertThat(underTest.inputFiles(underTest.predicates().all())).isEmpty(); - addFileWithAllStatus(); + addFilesWithAllStatus(); underTest.setRestrictToChangedFiles(false); assertThat(underTest.inputFiles(underTest.predicates().all())).hasSize(3); @@ -58,7 +67,7 @@ public class MutableFileSystemTest { @Test public void return_only_changed_files_when_restricted() { assertThat(underTest.inputFiles(underTest.predicates().all())).isEmpty(); - addFileWithAllStatus(); + addFilesWithAllStatus(); underTest.setRestrictToChangedFiles(true); assertThat(underTest.inputFiles(underTest.predicates().all())).hasSize(2); @@ -67,19 +76,95 @@ public class MutableFileSystemTest { assertThat(underTest.inputFile(underTest.predicates().hasFilename(generateFilename(InputFile.Status.CHANGED)))).isNotNull(); } - private void addFileWithAllStatus() { + @Test + public void return_all_files_when_allowing_hidden_files_analysis() { + assertThat(underTest.inputFiles(underTest.predicates().all())).isEmpty(); + addFilesWithVisibility(); + underTest.setAllowHiddenFileAnalysis(true); + + assertThat(underTest.inputFiles(underTest.predicates().all())).hasSize(2); + assertThat(underTest.inputFile(underTest.predicates().hasFilename(generateFilename(true)))).isNotNull(); + assertThat(underTest.inputFile(underTest.predicates().hasFilename(generateFilename(false)))).isNotNull(); + } + + @Test + public void return_only_non_hidden_files_when_not_allowing_hidden_files_analysis() { + assertThat(underTest.inputFiles(underTest.predicates().all())).isEmpty(); + addFilesWithVisibility(); + underTest.setAllowHiddenFileAnalysis(false); + + assertThat(underTest.inputFiles(underTest.predicates().all())).hasSize(1); + assertThat(underTest.inputFile(underTest.predicates().hasFilename(generateFilename(true)))).isNull(); + assertThat(underTest.inputFile(underTest.predicates().hasFilename(generateFilename(false)))).isNotNull(); + } + + @Test + public void hidden_file_predicate_should_preserve_predicate_optimization() { + addFilesWithVisibility(); + var anotherHiddenFile = spy(new TestInputFileBuilder("foo", String.format("src/%s", ".myHiddenFile.txt")) + .setLanguage(LANGUAGE).setStatus(InputFile.Status.ADDED).setHidden(true).build()); + underTest.add(anotherHiddenFile); + underTest.setAllowHiddenFileAnalysis(false); + + assertThat(underTest.inputFile(underTest.predicates().hasFilename(generateFilename(true)))).isNull(); + assertThat(underTest.inputFile(underTest.predicates().hasFilename(generateFilename(false)))).isNotNull(); + // Verify that predicate optimization is still effective + verify(anotherHiddenFile, never()).isHidden(); + + // This predicate can't be optimized + assertThat(underTest.inputFiles(underTest.predicates().all())).hasSize(1); + verify(anotherHiddenFile).isHidden(); + } + + @Test + public void hidden_file_predicate_should_be_applied_first_for_non_optimized_predicates() { + // Checking the file type is not very costly, but it is not optimized. In real life, something more costly would be reading the file + // content, for example. + addFilesWithVisibility(); + var anotherHiddenFile = spy(new TestInputFileBuilder("foo", String.format("src/%s", ".myHiddenFile." + LANGUAGE)) + .setLanguage(LANGUAGE).setType(InputFile.Type.MAIN).setStatus(InputFile.Status.ADDED).setHidden(true).build()); + underTest.add(anotherHiddenFile); + underTest.setAllowHiddenFileAnalysis(false); + + assertThat(underTest.inputFiles(underTest.predicates().hasType(InputFile.Type.MAIN))).hasSize(1); + // Verify that the file type has not been evaluated + verify(anotherHiddenFile, never()).type(); + } + + private void addFilesWithVisibility() { + addFile(true); + addFile(false); + } + + private void addFilesWithAllStatus() { addFile(InputFile.Status.ADDED); addFile(InputFile.Status.CHANGED); addFile(InputFile.Status.SAME); } private void addFile(InputFile.Status status) { - underTest.add(new TestInputFileBuilder("foo", String.format("src/%s", generateFilename(status))) - .setLanguage(LANGUAGE).setStatus(status).build()); + addFile(status, false); + } + + private void addFile(boolean hidden) { + addFile(InputFile.Status.SAME, hidden); + } + + private void addFile(InputFile.Status status, boolean hidden) { + underTest.add(new TestInputFileBuilder("foo", String.format("src/%s", generateFilename(status, hidden))) + .setLanguage(LANGUAGE).setType(InputFile.Type.MAIN).setStatus(status).setHidden(hidden).build()); + } + + private String generateFilename(boolean hidden) { + return generateFilename(InputFile.Status.SAME, hidden); } private String generateFilename(InputFile.Status status) { - return String.format("%s.%s", status.name().toLowerCase(Locale.ROOT), LANGUAGE); + return generateFilename(status, false); + } + + private String generateFilename(InputFile.Status status, boolean hidden) { + return String.format("%s.%s.%s", status.name().toLowerCase(Locale.ROOT), hidden, LANGUAGE); } } diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/DefaultSensorStorageTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/DefaultSensorStorageTest.java index e769e0d6451..355fb2cde0e 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/DefaultSensorStorageTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/DefaultSensorStorageTest.java @@ -19,16 +19,6 @@ */ package org.sonar.scanner.sensor; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.assertj.core.data.MapEntry.entry; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoInteractions; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.when; - import java.io.File; import java.util.ArrayList; import java.util.List; @@ -77,6 +67,16 @@ import org.sonar.scanner.repository.ContextPropertiesCache; import org.sonar.scanner.repository.TelemetryCache; import org.sonar.scanner.scan.branch.BranchConfiguration; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.assertj.core.data.MapEntry.entry; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + class DefaultSensorStorageTest { @TempDir @@ -97,7 +97,6 @@ class DefaultSensorStorageTest { public void prepare() { MetricFinder metricFinder = mock(MetricFinder.class); when(metricFinder.<Integer>findByKey(CoreMetrics.NCLOC_KEY)).thenReturn(CoreMetrics.NCLOC); - when(metricFinder.<String>findByKey(CoreMetrics.FUNCTION_COMPLEXITY_DISTRIBUTION_KEY)).thenReturn(CoreMetrics.FUNCTION_COMPLEXITY_DISTRIBUTION); when(metricFinder.<Integer>findByKey(CoreMetrics.LINES_TO_COVER_KEY)).thenReturn(CoreMetrics.LINES_TO_COVER); settings = new MapSettings(); 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 4ab9f46fb4a..3a0ff7fb4c2 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 @@ -68,7 +68,7 @@ class ModuleSensorContextTest { @BeforeEach void prepare() { fs = new DefaultFileSystem(temp); - underTest = new ModuleSensorContext(mock(DefaultInputProject.class), mock(InputModule.class), settings.asConfig(), settings, fs, activeRules, sensorStorage, runtime, + underTest = new ModuleSensorContext(mock(DefaultInputProject.class), mock(InputModule.class), settings.asConfig(), fs, activeRules, sensorStorage, runtime, branchConfiguration, writeCache, readCache, analysisCacheEnabled, unchangedFilesHandler, executingSensorContext, pluginRepository); } @@ -104,7 +104,7 @@ class ModuleSensorContextTest { @Test void pull_request_can_skip_unchanged_files() { when(branchConfiguration.isPullRequest()).thenReturn(true); - underTest = new ModuleSensorContext(mock(DefaultInputProject.class), mock(InputModule.class), settings.asConfig(), settings, fs, activeRules, sensorStorage, runtime, + underTest = new ModuleSensorContext(mock(DefaultInputProject.class), mock(InputModule.class), settings.asConfig(), fs, activeRules, sensorStorage, runtime, branchConfiguration, writeCache, readCache, analysisCacheEnabled, unchangedFilesHandler, executingSensorContext, pluginRepository); assertThat(underTest.canSkipUnchangedFiles()).isTrue(); } diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/ProjectSensorContextTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/ProjectSensorContextTest.java index 3c7f3d36793..01c337a5ed0 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/ProjectSensorContextTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/ProjectSensorContextTest.java @@ -59,8 +59,8 @@ class ProjectSensorContextTest { private ExecutingSensorContext executingSensorContext = mock(ExecutingSensorContext.class); private ScannerPluginRepository pluginRepository = mock(ScannerPluginRepository.class); - private ProjectSensorContext underTest = new ProjectSensorContext(mock(DefaultInputProject.class), settings.asConfig(), settings, fs, activeRules, sensorStorage, runtime, - branchConfiguration, writeCache, readCache, analysisCacheEnabled, unchangedFilesHandler, executingSensorContext, pluginRepository); + private ProjectSensorContext underTest = new ProjectSensorContext(mock(DefaultInputProject.class), settings.asConfig(), fs, activeRules, sensorStorage, runtime, + branchConfiguration, writeCache, readCache, analysisCacheEnabled, unchangedFilesHandler, executingSensorContext, pluginRepository); private static final String PLUGIN_KEY = "org.sonarsource.pluginKey"; @@ -69,7 +69,6 @@ class ProjectSensorContextTest { when(executingSensorContext.getSensorExecuting()).thenReturn(new SensorId(PLUGIN_KEY, "sensorName")); } - @Test void addTelemetryProperty_whenTheOrganizationIsSonarSource_mustStoreTheTelemetry() { @@ -77,16 +76,21 @@ class ProjectSensorContextTest { underTest.addTelemetryProperty("key", "value"); - //then verify that the defaultStorage is called with the telemetry property once + // then verify that the defaultStorage is called with the telemetry property once verify(sensorStorage).storeTelemetry("key", "value"); } @Test - void addTelemetryProperty_whenTheOrganizationIsNotSonarSource_mustThrowExcaption() { + void addTelemetryProperty_whenTheOrganizationIsNotSonarSource_mustThrowException() { when(pluginRepository.getPluginInfo(PLUGIN_KEY)).thenReturn(new PluginInfo(PLUGIN_KEY).setOrganizationName("notSonarsource")); assertThrows(IllegalStateException.class, () -> underTest.addTelemetryProperty("key", "value")); verifyNoInteractions(sensorStorage); } + + @Test + void settings_throwsUnsupportedOperationException() { + assertThrows(UnsupportedOperationException.class, () -> underTest.settings()); + } } diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scm/git/ChangedFileTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scm/git/ChangedFileTest.java index fe637015ed7..91f59b964b9 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scm/git/ChangedFileTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scm/git/ChangedFileTest.java @@ -88,7 +88,7 @@ public class ChangedFileTest { secure().next(5), Integer.parseInt(secure().nextNumeric(5)), new SensorStrategy(), - oldRelativePath); + oldRelativePath, false); } } diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scm/git/JGitUtilsTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scm/git/JGitUtilsTest.java index 383e2a1d643..d8264a5745b 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scm/git/JGitUtilsTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scm/git/JGitUtilsTest.java @@ -23,6 +23,7 @@ import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.List; +import org.apache.commons.lang.SystemUtils; import org.eclipse.jgit.api.Git; import org.eclipse.jgit.api.errors.GitAPIException; import org.junit.jupiter.api.Test; @@ -46,7 +47,11 @@ class JGitUtilsTest { // in directory1, the entire directory is ignored without listing each file // in directory2, specific files are ignored, so those files are listed // in directory3, specific files are ignored via a separate .gitignore file - assertThat(result).isEqualTo(List.of("directory1", "directory2/file_a.txt", "directory3/file_b.txt")); + if (SystemUtils.IS_OS_WINDOWS) { + assertThat(result).isEqualTo(List.of("directory1", "directory2\\file_a.txt", "directory3\\file_b.txt")); + } else { + assertThat(result).isEqualTo(List.of("directory1", "directory2/file_a.txt", "directory3/file_b.txt")); + } } @Test diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scm/git/NativeGitBlameCommandTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scm/git/NativeGitBlameCommandTest.java index 68b7e1b7bd2..23d5e6ca5f0 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scm/git/NativeGitBlameCommandTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scm/git/NativeGitBlameCommandTest.java @@ -159,14 +159,14 @@ class NativeGitBlameCommandTest { ProcessWrapperFactory mockFactory = mock(ProcessWrapperFactory.class); ProcessWrapper mockProcess = mock(ProcessWrapper.class); String gitCommand = "git"; - when(mockFactory.create(any(), any(), anyString(), anyString(), anyString(), anyString(), + when(mockFactory.create(any(), any(), any(), anyString(), anyString(), anyString(), anyString(), anyString(), anyString(), anyString(), anyString(), anyString(), anyString())) - .then(invocation -> mockProcess); + .then(invocation -> mockProcess); NativeGitBlameCommand blameCommand = new NativeGitBlameCommand(gitCommand, System2.INSTANCE, mockFactory); blameCommand.blame(baseDir.toPath(), DUMMY_JAVA); - verify(mockFactory).create(any(), any(), eq(gitCommand), + verify(mockFactory).create(any(), any(), any(), eq(gitCommand), eq(GIT_DIR_FLAG), eq(String.format(GIT_DIR_ARGUMENT, baseDir.toPath())), eq(GIT_DIR_FORCE_FLAG), @@ -238,7 +238,7 @@ class NativeGitBlameCommandTest { "git version 2.25.1.msysgit.2").forEach(output -> { ProcessWrapperFactory mockedCmd = mockGitVersionCommand(output); mockGitWhereOnWindows(mockedCmd); - when(mockedCmd.create(isNull(), any(), eq("C:\\mockGit.exe"), eq("--version"))).then(invocation -> { + when(mockedCmd.create(isNull(), any(), any(), eq("C:\\mockGit.exe"), eq("--version"))).then(invocation -> { var argument = (Consumer<String>) invocation.getArgument(1); argument.accept(output); return mock(ProcessWrapper.class); @@ -303,7 +303,7 @@ class NativeGitBlameCommandTest { ProcessWrapper mockProcess = mock(ProcessWrapper.class); mockGitWhereOnWindows(mockFactory); - when(mockFactory.create(isNull(), any(), eq("C:\\mockGit.exe"), eq("--version"))).then(invocation -> { + when(mockFactory.create(isNull(), any(), any(), eq("C:\\mockGit.exe"), eq("--version"))).then(invocation -> { var argument = (Consumer<String>) invocation.getArgument(1); argument.accept("git version 2.30.1"); return mockProcess; @@ -346,7 +346,7 @@ class NativeGitBlameCommandTest { } private void mockGitWhereOnWindows(ProcessWrapperFactory processWrapperFactory) { - when(processWrapperFactory.create(isNull(), any(), eq("C:\\Windows\\System32\\where.exe"), eq("$PATH:git.exe"))).then(invocation -> { + when(processWrapperFactory.create(isNull(), any(), any(), eq("C:\\Windows\\System32\\where.exe"), eq("$PATH:git.exe"))).then(invocation -> { var argument = (Consumer<String>) invocation.getArgument(1); argument.accept("C:\\mockGit.exe"); return mock(ProcessWrapper.class); @@ -357,7 +357,7 @@ class NativeGitBlameCommandTest { ProcessWrapperFactory mockFactory = mock(ProcessWrapperFactory.class); ProcessWrapper mockProcess = mock(ProcessWrapper.class); - when(mockFactory.create(isNull(), any(), eq("git"), eq("--version"))).then(invocation -> { + when(mockFactory.create(isNull(), any(), any(), eq("git"), eq("--version"))).then(invocation -> { var argument = (Consumer<String>) invocation.getArgument(1); argument.accept(commandOutput); return mockProcess; diff --git a/sonar-scanner-engine/src/test/resources/org/sonar/scanner/sca/echo_args.bat b/sonar-scanner-engine/src/test/resources/org/sonar/scanner/sca/echo_args.bat index 5677cf5c437..577375b330d 100644 --- a/sonar-scanner-engine/src/test/resources/org/sonar/scanner/sca/echo_args.bat +++ b/sonar-scanner-engine/src/test/resources/org/sonar/scanner/sca/echo_args.bat @@ -6,7 +6,7 @@ set "POSITIONAL_ARGS=" :loop if "%~1"=="" goto endloop -if "%~1"=="--zip-filename" ( +if "%~1"=="--xz-filename" ( set "FILENAME=%~2" shift shift diff --git a/sonar-scanner-engine/src/test/resources/org/sonar/scanner/sca/echo_args.sh b/sonar-scanner-engine/src/test/resources/org/sonar/scanner/sca/echo_args.sh index 881be2eaac5..f7feed1f501 100755 --- a/sonar-scanner-engine/src/test/resources/org/sonar/scanner/sca/echo_args.sh +++ b/sonar-scanner-engine/src/test/resources/org/sonar/scanner/sca/echo_args.sh @@ -6,7 +6,7 @@ POSITIONAL_ARGS=() while [[ $# -gt 0 ]]; do case $1 in - --zip-filename) + --xz-filename) FILENAME="$2" shift shift |