diff --git a/its/plugin/tests/src/test/java/com/sonar/it/java/suite/JavaClasspathTest.java b/its/plugin/tests/src/test/java/com/sonar/it/java/suite/JavaClasspathTest.java index 8ce3fdf24b3..1f9dd82bf4c 100644 --- a/its/plugin/tests/src/test/java/com/sonar/it/java/suite/JavaClasspathTest.java +++ b/its/plugin/tests/src/test/java/com/sonar/it/java/suite/JavaClasspathTest.java @@ -92,8 +92,8 @@ public void invalid_binaries_dir_should_fail_analysis() { SonarScanner scanner = ditProjectSonarScanner(); scanner.setProperty("sonar.java.binaries", "target/dummy__Dir"); BuildResult buildResult = ORCHESTRATOR.executeBuildQuietly(scanner); - assertThat(buildResult.getLastStatus()).isNotZero(); - assertThat(buildResult.getLogs()).contains("No files nor directories matching 'target/dummy__Dir'"); + assertThat(buildResult.getLastStatus()).isZero(); + assertThat(buildResult.getLogs()).contains("WARN Invalid value for 'sonar.java.binaries', no files nor directories matching 'target/dummy__Dir'."); } @Test @@ -154,27 +154,13 @@ public void should_keep_order_libs() { assertThat(getNumberOfViolations(projectKey)).isEqualTo(1); } - @Test - public void should_support_the_old_binaries_and_libraries_properties() { - SonarScanner scanner = ditProjectSonarScanner(); - scanner.setProperty("sonar.binaries", "target/classes"); - scanner.setProperty("sonar.libraries", guavaJarPathEscaped); - BuildResult buildResult = ORCHESTRATOR.executeBuildQuietly(scanner); - - assertThat(buildResult.getLogs()).contains( - "sonar.binaries and sonar.libraries are not supported since version 4.0 of the SonarSource Java Analyzer," + - " please use sonar.java.binaries and sonar.java.libraries instead"); - assertThat(buildResult.isSuccess()).isFalse(); - } - @Test public void should_log_warnings_if_binaries_missing() { SonarScanner scanner = ditProjectSonarScanner(); BuildResult buildResult = ORCHESTRATOR.executeBuildQuietly(scanner); String logs = buildResult.getLogs(); - assertThat(logs).contains("Your project contains .java files, please provide compiled classes with sonar.java.binaries property," - + " or exclude them from the analysis with sonar.exclusions property."); - assertThat(buildResult.isSuccess()).isFalse(); + assertThat(logs).contains("WARN Missing 'sonar.java.binaries' and 'sonar.java.libraries' properties. You might end up with less precise analysis results."); + assertThat(buildResult.isSuccess()).isTrue(); } @Test diff --git a/java-frontend/src/main/java/org/sonar/java/SonarComponents.java b/java-frontend/src/main/java/org/sonar/java/SonarComponents.java index e12c282caa6..240e2d012d9 100644 --- a/java-frontend/src/main/java/org/sonar/java/SonarComponents.java +++ b/java-frontend/src/main/java/org/sonar/java/SonarComponents.java @@ -616,11 +616,11 @@ public void logUndefinedTypes() { if (problemsToFilePaths.isEmpty()) { return; } - javaClasspath.logSuspiciousEmptyLibraries(); + javaClasspath.logClasspathWarnings(); if (!isAutoScan()) { // In autoscan, test + main code are analyzed in the same batch, and we do not make the distinction between // test and main libraries, everything is inside "sonar.java.libraries", it is expected to let the test property empty. - javaTestClasspath.logSuspiciousEmptyLibraries(); + javaTestClasspath.logClasspathWarnings(); } logUndefinedTypes(LOGGED_MAX_NUMBER_UNDEFINED_TYPES); diff --git a/java-frontend/src/main/java/org/sonar/java/classpath/AbstractClasspath.java b/java-frontend/src/main/java/org/sonar/java/classpath/AbstractClasspath.java index 56ed20e100f..9c965cfa245 100644 --- a/java-frontend/src/main/java/org/sonar/java/classpath/AbstractClasspath.java +++ b/java-frontend/src/main/java/org/sonar/java/classpath/AbstractClasspath.java @@ -33,6 +33,7 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; import java.util.Optional; @@ -42,10 +43,12 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sonar.api.batch.ScannerSide; +import org.sonar.api.batch.fs.FilePredicate; import org.sonar.api.batch.fs.FileSystem; import org.sonar.api.batch.fs.InputFile; import org.sonar.api.config.Configuration; -import org.sonar.java.collections.CollectionUtils; +import org.sonar.java.AnalysisWarningsWrapper; +import org.sonar.java.SonarComponents; import org.sonarsource.api.sonarlint.SonarLintSide; @ScannerSide @@ -58,26 +61,67 @@ public abstract class AbstractClasspath { protected final Configuration settings; protected final FileSystem fs; private final InputFile.Type fileType; + protected final String binariesProperty; + protected final String librariesProperty; private static final Path[] STANDARD_CLASSES_DIRS = {Paths.get("target", "classes"), Paths.get("target", "test-classes")}; protected final List binaries; - protected final List elements; + protected List elements; protected boolean validateLibraries; protected boolean initialized; private boolean inAndroidContext = false; + private final Set classpathWarnings; + protected final AnalysisWarningsWrapper analysisWarnings; - protected AbstractClasspath(Configuration settings, FileSystem fs, InputFile.Type fileType) { + protected AbstractClasspath(Configuration settings, FileSystem fs, InputFile.Type fileType, String binariesProperty, String librariesProperty, + AnalysisWarningsWrapper analysisWarnings) { this.settings = settings; this.fs = fs; this.fileType = fileType; + this.binariesProperty = binariesProperty; + this.librariesProperty = librariesProperty; this.binaries = new ArrayList<>(); - this.elements = new ArrayList<>(); + this.elements = List.of(); + this.analysisWarnings = analysisWarnings; + classpathWarnings = new LinkedHashSet<>(); initialized = false; } + protected void init() { + if (!initialized) { + initialized = true; + validateLibraries = hasJavaFiles(); + validatePropertiesPresence(); + binaries.addAll(getFilesFromProperty(binariesProperty)); + Set libraries = new LinkedHashSet<>(getJdkJars()); + Set extraLibraries = getFilesFromProperty(librariesProperty); + libraries.addAll(extraLibraries); + logResolvedFiles(binariesProperty, binaries); + logResolvedFiles(librariesProperty, libraries); + Set all = new LinkedHashSet<>(binaries); + all.addAll(libraries); + elements = List.copyOf(all); + } + } + + protected void validatePropertiesPresence() { + if (settings.getBoolean(SonarComponents.SONAR_AUTOSCAN).orElse(false)) { + return; + } + boolean missingBinary = !settings.hasKey(binariesProperty) && hasMoreThanOneJavaFile(); + boolean missingLibraries = !settings.hasKey(librariesProperty) && hasJavaFiles(); + if (missingBinary && missingLibraries) { + classpathWarnings.add(String.format("Missing '%s' and '%s' properties. You might end up with less precise analysis results.", binariesProperty, librariesProperty)); + } else if (missingBinary) { + classpathWarnings.add(String.format("Missing '%s' property. You might end up with less precise analysis results.", binariesProperty)); + } else if (missingLibraries) { + classpathWarnings.add(String.format("Missing '%s' property. You might end up with less precise analysis results.", librariesProperty)); + } + } + protected List getJdkJars() { List jdkClassesRoots = settings.get(ClasspathProperties.SONAR_JAVA_JDK_HOME) - .flatMap(AbstractClasspath::existingDirectoryOrLog) + .flatMap(this::existingDirectoryOrLog) .map(File::toPath) .map(JavaSdkUtil::getJdkClassesRoots) .orElse(Collections.emptyList()); @@ -87,26 +131,29 @@ protected List getJdkJars() { static void logResolvedFiles(String property, Collection files) { if (LOG.isDebugEnabled()) { - LOG.debug(String.format("Property '%s' resolved with:%n%s", property, files.stream() + LOG.debug(String.format("Property '%s' resolved with: %s", property, files.stream() .map(File::getAbsolutePath) - .collect(Collectors.joining("," + System.lineSeparator(), "[", "]")))); + .collect(Collectors.joining(",", "[", "]")))); } } - private static Optional existingDirectoryOrLog(String path) { + private Optional existingDirectoryOrLog(String path) { LOG.debug("Property '{}' set with: {}", ClasspathProperties.SONAR_JAVA_JDK_HOME, path); File file = new File(path); if (!file.exists() || !file.isDirectory()) { - LOG.warn("Invalid value for '{}' property, defaulting to runtime JDK.{}Configured location does not exists: '{}'", - ClasspathProperties.SONAR_JAVA_JDK_HOME, System.lineSeparator(), file.getAbsolutePath()); + classpathWarnings.add(String.format("Invalid value '%s' for '%s' property, defaulting to runtime JDK.", file.getAbsolutePath(), ClasspathProperties.SONAR_JAVA_JDK_HOME)); return Optional.empty(); } return Optional.of(file); } - protected abstract void init(); - - public abstract void logSuspiciousEmptyLibraries(); + public void logClasspathWarnings() { + for (String warning : classpathWarnings) { + LOG.warn(warning); + analysisWarnings.addUnique(warning); + } + classpathWarnings.clear(); + } protected Set getFilesFromProperty(String property) { Set result = new LinkedHashSet<>(); @@ -114,15 +161,13 @@ protected Set getFilesFromProperty(String property) { List fileNames = Arrays.stream(settings.getStringArray(property)) .filter(s -> !s.isEmpty()).toList(); File baseDir = fs.baseDir(); - boolean hasJavaSources = hasJavaSources(); + boolean hasJavaFiles = hasJavaFiles(); boolean validateLibs = validateLibraries; boolean isLibraryProperty = property.endsWith("libraries"); for (String pathPattern : fileNames) { Set libraryFilesForPattern = getFilesForPattern(baseDir.toPath(), pathPattern, isLibraryProperty); - if (validateLibraries && libraryFilesForPattern.isEmpty() && hasJavaSources) { - LOG.error("Invalid value for '{}' property.", property); - String message = "No files nor directories matching '" + pathPattern + "'"; - throw new IllegalStateException(message); + if (validateLibraries && libraryFilesForPattern.isEmpty() && hasJavaFiles) { + classpathWarnings.add(String.format("Invalid value for '%s', no files nor directories matching '%s'.", property, pathPattern)); } validateLibraries = validateLibs; result.addAll(libraryFilesForPattern); @@ -134,12 +179,22 @@ protected Set getFilesFromProperty(String property) { return result; } - protected boolean hasJavaSources() { - return fs.hasFiles(fs.predicates().and(fs.predicates().hasLanguage("java"), fs.predicates().hasType(fileType))); + protected boolean hasMoreThanOneJavaFile() { + // No need to iterate over the entire collection, checking that there are two elements is enough + Iterator iterator = fs.inputFiles(sourcePredicate()).iterator(); + if (iterator.hasNext()) { + iterator.next(); + return iterator.hasNext(); + } + return false; } - protected boolean hasMoreThanOneJavaFile() { - return CollectionUtils.size(fs.inputFiles(fs.predicates().and(fs.predicates().hasLanguage("java"), fs.predicates().hasType(fileType)))) > 1; + protected boolean hasJavaFiles() { + return fs.hasFiles(sourcePredicate()); + } + + private FilePredicate sourcePredicate() { + return fs.predicates().and(fs.predicates().hasLanguage("java"), fs.predicates().hasType(fileType)); } private Set getFilesForPattern(Path baseDir, String pathPattern, boolean libraryProperty) { diff --git a/java-frontend/src/main/java/org/sonar/java/classpath/ClasspathForMain.java b/java-frontend/src/main/java/org/sonar/java/classpath/ClasspathForMain.java index 628cead2d48..091713ace85 100644 --- a/java-frontend/src/main/java/org/sonar/java/classpath/ClasspathForMain.java +++ b/java-frontend/src/main/java/org/sonar/java/classpath/ClasspathForMain.java @@ -16,86 +16,22 @@ */ package org.sonar.java.classpath; -import java.io.File; -import java.util.LinkedHashSet; -import java.util.Set; -import javax.annotation.Nullable; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.sonar.api.batch.fs.FileSystem; import org.sonar.api.batch.fs.InputFile; import org.sonar.api.config.Configuration; -import org.sonar.java.AnalysisException; import org.sonar.java.AnalysisWarningsWrapper; -public class ClasspathForMain extends AbstractClasspath { - - private static final Logger LOG = LoggerFactory.getLogger(ClasspathForMain.class); +import static org.sonar.java.classpath.ClasspathProperties.SONAR_JAVA_BINARIES; +import static org.sonar.java.classpath.ClasspathProperties.SONAR_JAVA_LIBRARIES; - private final AnalysisWarningsWrapper analysisWarnings; - private boolean hasSuspiciousEmptyLibraries = false; - private boolean alreadyReported = false; +public class ClasspathForMain extends AbstractClasspath { public ClasspathForMain(Configuration settings, FileSystem fs, AnalysisWarningsWrapper analysisWarnings) { - super(settings, fs, InputFile.Type.MAIN); - this.analysisWarnings = analysisWarnings; + super(settings, fs, InputFile.Type.MAIN, SONAR_JAVA_BINARIES, SONAR_JAVA_LIBRARIES, analysisWarnings); } public ClasspathForMain(Configuration settings, FileSystem fs) { this(settings, fs, AnalysisWarningsWrapper.NOOP_ANALYSIS_WARNINGS); } - @Override - protected void init() { - if (!initialized) { - validateLibraries = fs.hasFiles(fs.predicates().all()); - initialized = true; - binaries.addAll(getFilesFromProperty(ClasspathProperties.SONAR_JAVA_BINARIES)); - - Set libraries = new LinkedHashSet<>(getJdkJars()); - Set extraLibraries = getFilesFromProperty(ClasspathProperties.SONAR_JAVA_LIBRARIES); - logResolvedFiles(ClasspathProperties.SONAR_JAVA_LIBRARIES, extraLibraries); - libraries.addAll(extraLibraries); - if (binaries.isEmpty() && libraries.isEmpty() && useDeprecatedProperties()) { - throw new AnalysisException( - "sonar.binaries and sonar.libraries are not supported since version 4.0 of the SonarSource Java Analyzer," - + " please use sonar.java.binaries and sonar.java.libraries instead"); - } - hasSuspiciousEmptyLibraries = libraries.isEmpty() && hasJavaSources(); - - if (binaries.isEmpty() && hasMoreThanOneJavaFile()) { - if(isSonarLint()) { - LOG.warn("sonar.java.binaries is empty, please double check your configuration"); - } else { - throw new AnalysisException("Your project contains .java files, please provide compiled classes with sonar.java.binaries property," - + " or exclude them from the analysis with sonar.exclusions property."); - } - } - - elements.addAll(binaries); - elements.addAll(libraries); - } - } - - protected boolean isSonarLint() { - return false; - } - - private boolean useDeprecatedProperties() { - return isNotNullOrEmpty(settings.get("sonar.binaries").orElse(null)) && isNotNullOrEmpty(settings.get("sonar.libraries").orElse(null)); - } - - private static boolean isNotNullOrEmpty(@Nullable String string) { - return string != null && !string.isEmpty(); - } - - @Override - public void logSuspiciousEmptyLibraries() { - if (hasSuspiciousEmptyLibraries && !alreadyReported) { - String warning = String.format(ClasspathProperties.EMPTY_LIBRARIES_WARNING_TEMPLATE, "SOURCE", ClasspathProperties.SONAR_JAVA_LIBRARIES); - LOG.warn(warning); - analysisWarnings.addUnique(warning); - alreadyReported = true; - } - } } diff --git a/java-frontend/src/main/java/org/sonar/java/classpath/ClasspathForMainForSonarLint.java b/java-frontend/src/main/java/org/sonar/java/classpath/ClasspathForMainForSonarLint.java deleted file mode 100644 index 7235ee0aac8..00000000000 --- a/java-frontend/src/main/java/org/sonar/java/classpath/ClasspathForMainForSonarLint.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * SonarQube Java - * Copyright (C) SonarSource Sàrl - * mailto:info AT sonarsource DOT com - * - * You can redistribute and/or modify this program under the terms of - * the Sonar Source-Available License Version 1, as published by SonarSource Sàrl. - * - * 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 Sonar Source-Available License for more details. - * - * You should have received a copy of the Sonar Source-Available License - * along with this program; if not, see https://sonarsource.com/license/ssal/ - */ -package org.sonar.java.classpath; - -import org.sonar.api.batch.fs.FileSystem; -import org.sonar.api.config.Configuration; - -public class ClasspathForMainForSonarLint extends ClasspathForMain { - - public ClasspathForMainForSonarLint(Configuration settings, FileSystem fs) { - super(settings, fs); - } - - @Override - protected boolean isSonarLint() { - return true; - } - - -} diff --git a/java-frontend/src/main/java/org/sonar/java/classpath/ClasspathForTest.java b/java-frontend/src/main/java/org/sonar/java/classpath/ClasspathForTest.java index 4923ff538b0..72ebe7ce344 100644 --- a/java-frontend/src/main/java/org/sonar/java/classpath/ClasspathForTest.java +++ b/java-frontend/src/main/java/org/sonar/java/classpath/ClasspathForTest.java @@ -16,51 +16,22 @@ */ package org.sonar.java.classpath; -import java.io.File; -import java.util.LinkedHashSet; -import java.util.Set; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.sonar.api.batch.fs.FileSystem; import org.sonar.api.batch.fs.InputFile; import org.sonar.api.config.Configuration; +import org.sonar.java.AnalysisWarningsWrapper; -public class ClasspathForTest extends AbstractClasspath { - - private static final Logger LOG = LoggerFactory.getLogger(ClasspathForTest.class); - - private boolean hasSuspiciousEmptyLibraries = false; - private boolean alreadyReported = false; +import static org.sonar.java.classpath.ClasspathProperties.SONAR_JAVA_TEST_BINARIES; +import static org.sonar.java.classpath.ClasspathProperties.SONAR_JAVA_TEST_LIBRARIES; - public ClasspathForTest(Configuration settings, FileSystem fs) { - super(settings, fs, InputFile.Type.TEST); - } - - @Override - protected void init() { - if (!initialized) { - validateLibraries = fs.hasFiles(fs.predicates().all()); - initialized = true; - binaries.addAll(getFilesFromProperty(ClasspathProperties.SONAR_JAVA_TEST_BINARIES)); - - Set libraries = new LinkedHashSet<>(getJdkJars()); - Set extraLibraries = getFilesFromProperty(ClasspathProperties.SONAR_JAVA_TEST_LIBRARIES); - logResolvedFiles(ClasspathProperties.SONAR_JAVA_TEST_LIBRARIES, extraLibraries); - libraries.addAll(extraLibraries); - hasSuspiciousEmptyLibraries = libraries.isEmpty() && hasJavaSources(); +public class ClasspathForTest extends AbstractClasspath { - elements.addAll(binaries); - elements.addAll(libraries); - } + public ClasspathForTest(Configuration settings, FileSystem fs, AnalysisWarningsWrapper analysisWarnings) { + super(settings, fs, InputFile.Type.TEST, SONAR_JAVA_TEST_BINARIES, SONAR_JAVA_TEST_LIBRARIES, analysisWarnings); } - @Override - public void logSuspiciousEmptyLibraries() { - if (hasSuspiciousEmptyLibraries && !alreadyReported) { - String warning = String.format(ClasspathProperties.EMPTY_LIBRARIES_WARNING_TEMPLATE, "TEST", ClasspathProperties.SONAR_JAVA_TEST_LIBRARIES); - LOG.warn(warning); - alreadyReported = true; - } + public ClasspathForTest(Configuration settings, FileSystem fs) { + this(settings, fs, AnalysisWarningsWrapper.NOOP_ANALYSIS_WARNINGS); } } diff --git a/java-frontend/src/main/java/org/sonar/java/classpath/ClasspathProperties.java b/java-frontend/src/main/java/org/sonar/java/classpath/ClasspathProperties.java index 7ba8364ac55..6231c46183d 100644 --- a/java-frontend/src/main/java/org/sonar/java/classpath/ClasspathProperties.java +++ b/java-frontend/src/main/java/org/sonar/java/classpath/ClasspathProperties.java @@ -24,9 +24,6 @@ public class ClasspathProperties { - public static final String EMPTY_LIBRARIES_WARNING_TEMPLATE = "Dependencies/libraries were not provided for analysis of %s files. The '%s' property is empty." + - " Verify your configuration, as you might end up with less precise results."; - public static final String SONAR_JAVA_JDK_HOME = "sonar.java.jdkHome"; public static final String SONAR_JAVA_BINARIES = "sonar.java.binaries"; diff --git a/java-frontend/src/test/java/org/sonar/java/SonarComponentsTest.java b/java-frontend/src/test/java/org/sonar/java/SonarComponentsTest.java index c6cbb27a4d1..8af9156dafe 100644 --- a/java-frontend/src/test/java/org/sonar/java/SonarComponentsTest.java +++ b/java-frontend/src/test/java/org/sonar/java/SonarComponentsTest.java @@ -1084,10 +1084,10 @@ class Logging { private final SensorContextTester context = SensorContextTester.create(new File("")); private final DefaultFileSystem fs = context.fileSystem(); - private final Configuration settings = context.config(); + private final MapSettings settings = context.settings(); - private final ClasspathForMain javaClasspath = new ClasspathForMain(settings, fs); - private final ClasspathForTest javaTestClasspath = new ClasspathForTest(settings, fs); + private final ClasspathForMain javaClasspath = new ClasspathForMain(context.config(), fs); + private final ClasspathForTest javaTestClasspath = new ClasspathForTest(context.config(), fs); private SonarComponents sonarComponents; @@ -1175,26 +1175,24 @@ void suspicious_empty_libraries_should_be_logged() { logTester.setLevel(Level.INFO); logUndefinedTypesWithOneMainAndOneTest(); - assertThat(logTester.logs(Level.WARN)) - .contains("Dependencies/libraries were not provided for analysis of SOURCE files. The 'sonar.java.libraries' property is empty. " + - "Verify your configuration, as you might end up with less precise results.") - .contains("Dependencies/libraries were not provided for analysis of TEST files. " + - "The 'sonar.java.test.libraries' property is empty. Verify your configuration, as you might end up with less precise results."); + assertThat(logTester.logs(Level.WARN)).containsExactly( + "Missing 'sonar.java.libraries' property. You might end up with less precise analysis results.", + "Missing 'sonar.java.test.libraries' property. You might end up with less precise analysis results.", + "Unresolved imports/types have been detected during analysis. Enable DEBUG mode to see them." + ); } @Test void suspicious_empty_libraries_should_not_be_logged_in_autoscan() { logTester.setLevel(Level.INFO); // Enable autoscan with a property - context.setSettings(new MapSettings().setProperty(SonarComponents.SONAR_AUTOSCAN, true)); + settings.setProperty(SonarComponents.SONAR_AUTOSCAN, true); logUndefinedTypesWithOneMainAndOneTest(); - assertThat(logTester.logs(Level.WARN)) - .contains("Dependencies/libraries were not provided for analysis of SOURCE files. The 'sonar.java.libraries' property is empty. " + - "Verify your configuration, as you might end up with less precise results.") - .doesNotContain("Dependencies/libraries were not provided for analysis of TEST files. " + - "The 'sonar.java.test.libraries' property is empty. Verify your configuration, as you might end up with less precise results."); + assertThat(logTester.logs(Level.WARN)).containsExactly( + "Unresolved imports/types have been detected during analysis. Enable DEBUG mode to see them." + ); } @Test diff --git a/java-frontend/src/test/java/org/sonar/java/classpath/ClasspathForMainTest.java b/java-frontend/src/test/java/org/sonar/java/classpath/ClasspathForMainTest.java index b7a4d206289..e0002b296d2 100644 --- a/java-frontend/src/test/java/org/sonar/java/classpath/ClasspathForMainTest.java +++ b/java-frontend/src/test/java/org/sonar/java/classpath/ClasspathForMainTest.java @@ -17,7 +17,13 @@ package org.sonar.java.classpath; import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; import java.util.List; +import org.jspecify.annotations.NonNull; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; @@ -31,34 +37,35 @@ import org.sonar.api.config.internal.MapSettings; import org.sonar.api.config.internal.MultivalueProperty; import org.sonar.api.utils.System2; -import org.sonar.java.AnalysisException; import org.sonar.java.AnalysisWarningsWrapper; +import org.sonar.java.SonarComponents; import org.sonar.java.TestUtils; import org.sonar.java.testing.ThreadLocalLogTester; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatCode; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoInteractions; class ClasspathForMainTest { + private static final Path RELATIVE_BASE_PATH = Paths.get("src", "test", "files", "classpath"); + private static final Path RELATIVE_JDK_PATH = Paths.get("src", "test", "jdk"); + private Path baseDir; + private Path jdkPath; private MapSettings settings; private DefaultFileSystem fs; private AnalysisWarningsWrapper analysisWarnings; - private ClasspathForMain javaClasspath; - @RegisterExtension public ThreadLocalLogTester logTester = new ThreadLocalLogTester().setLevel(Level.DEBUG); + public List analysisWarningsLogged = new ArrayList<>(); @BeforeEach - void setup() { - fs = new DefaultFileSystem(new File("src/test/files/classpath/")); + void setup() throws IOException { + baseDir = RELATIVE_BASE_PATH.toRealPath(); + jdkPath = RELATIVE_JDK_PATH.toRealPath(); + Files.createDirectories(baseDir.resolve("empty")); + fs = new DefaultFileSystem(baseDir); fs.add(TestUtils.emptyInputFile("foo.java")); PropertyDefinitions propertyDefinitions = new PropertyDefinitions(System2.INSTANCE); ClasspathProperties.getProperties().forEach(propertyDefinitions::addComponent); @@ -67,13 +74,15 @@ void setup() { * MapSettings doesn't support CSV encoded properties, but real scanner component does (see org/sonar/scanner/config/DefaultConfiguration) */ @Override - public String[] getStringArray(String key) { + public String[] getStringArray(@NonNull String key) { return get(key) .map(v -> MultivalueProperty.parseAsCsv(key, v)) .orElse(new String[0]); } }; - analysisWarnings = mock(AnalysisWarningsWrapper.class); + settings.setProperty(ClasspathProperties.SONAR_JAVA_BINARIES, "empty"); + settings.setProperty(ClasspathProperties.SONAR_JAVA_LIBRARIES, "empty"); + analysisWarnings = new AnalysisWarningsWrapper(text -> analysisWarningsLogged.add(text)); } /** @@ -84,9 +93,10 @@ public String[] getStringArray(String key) { @Test void no_interaction_with_FileSystem_at_initialization() { fs = spy(new DefaultFileSystem(new File("src/test/files/classpath/"))); - javaClasspath = createJavaClasspath(); + createJavaClasspath(); verifyNoInteractions(fs); - verifyNoInteractions(analysisWarnings); + assertThat(logTester.logs()).isEmpty(); + assertThat(analysisWarningsLogged).isEmpty(); } @Test @@ -95,326 +105,372 @@ void properties() { } @Test - void when_property_not_defined_project_classpath_null_getElements_should_be_empty() { - javaClasspath = createJavaClasspath(); - assertThat(javaClasspath.getElements()).isEmpty(); + void when_binaries_and_libraries_not_defined_classpath_getElements_should_be_empty_with_a_warning() { + settings.removeProperty(ClasspathProperties.SONAR_JAVA_BINARIES); + settings.removeProperty(ClasspathProperties.SONAR_JAVA_LIBRARIES); + createTwoFilesInFileSystem(); + ClassPathResult actual = createAndInitClasspath(); + assertThat(actual.elements()).isEmpty(); + String warning = "Missing 'sonar.java.binaries' and 'sonar.java.libraries' properties. You might end up with less precise analysis results."; + assertThat(actual.projectWarnings()).containsExactly(warning); + assertThat(actual.logs()).containsExactly( + "DEBUG Property 'sonar.java.jdkHome' resolved with: []", + "DEBUG Property 'sonar.java.binaries' resolved with: []", + "DEBUG Property 'sonar.java.libraries' resolved with: []", + "WARN " + warning + ); } @Test - void display_warning_for_missing_bytecode_when_libraries_empty_and_have_java_sources() { - javaClasspath = createJavaClasspath(); - javaClasspath.init(); - assertThat(javaClasspath.getFilesFromProperty(ClasspathProperties.SONAR_JAVA_LIBRARIES)).isEmpty(); - assertThat(javaClasspath.hasJavaSources()).isTrue(); - - javaClasspath.logSuspiciousEmptyLibraries(); - - String warning = "Dependencies/libraries were not provided for analysis of SOURCE files. The 'sonar.java.libraries' property is empty. " - + "Verify your configuration, as you might end up with less precise results."; - verify(analysisWarnings).addUnique(warning); - assertThat(logTester.logs(Level.WARN)).containsExactly(warning); + void when_libraries_property_not_defined_classpath_log_a_warning() { + settings.removeProperty(ClasspathProperties.SONAR_JAVA_LIBRARIES); + createTwoFilesInFileSystem(); + ClassPathResult actual = createAndInitClasspath(); + assertThat(actual.elements()).containsExactly("/empty"); + String warning = "Missing 'sonar.java.libraries' property. You might end up with less precise analysis results."; + assertThat(actual.projectWarnings()).containsExactly(warning); + assertThat(actual.logs()).containsExactly( + "DEBUG Property 'sonar.java.jdkHome' resolved with: []", + "DEBUG Property 'sonar.java.binaries' resolved with: [/empty]", + "DEBUG Property 'sonar.java.libraries' resolved with: []", + "WARN " + warning + ); } @Test - void only_display_once_warning_for_missing_bytecode_when_libraries_empty_and_have_java_sources() { - javaClasspath = createJavaClasspath(); - javaClasspath.init(); - assertThat(javaClasspath.getFilesFromProperty(ClasspathProperties.SONAR_JAVA_LIBRARIES)).isEmpty(); - assertThat(javaClasspath.hasJavaSources()).isTrue(); - - javaClasspath.logSuspiciousEmptyLibraries(); - - // re-trigger logs - javaClasspath.logSuspiciousEmptyLibraries(); - - String warning = "Dependencies/libraries were not provided for analysis of SOURCE files. The 'sonar.java.libraries' property is empty. " - + "Verify your configuration, as you might end up with less precise results."; - verify(analysisWarnings, times(1)).addUnique(warning); - assertThat(logTester.logs(Level.WARN)).containsExactly(warning); + void when_binaries_property_not_defined_classpath_log_a_warning_with_more_than_one_java_file() { + settings.removeProperty(ClasspathProperties.SONAR_JAVA_BINARIES); + createTwoFilesInFileSystem(); + ClassPathResult actual = createAndInitClasspath(); + assertThat(actual.elements()).containsExactly("/empty"); + String warning = "Missing 'sonar.java.binaries' property. You might end up with less precise analysis results."; + assertThat(actual.projectWarnings()).containsExactly(warning); + assertThat(actual.logs()).containsExactly( + "DEBUG Property 'sonar.java.jdkHome' resolved with: []", + "DEBUG Property 'sonar.java.binaries' resolved with: []", + "DEBUG Property 'sonar.java.libraries' resolved with: [/empty]", + "WARN " + warning + ); + } + + @Test + void when_binaries_and_libraries_not_defined_classpath_log_no_warning_for_autoscan() { + settings.removeProperty(ClasspathProperties.SONAR_JAVA_BINARIES); + settings.removeProperty(ClasspathProperties.SONAR_JAVA_LIBRARIES); + settings.setProperty(SonarComponents.SONAR_AUTOSCAN, true); + createTwoFilesInFileSystem(); + ClassPathResult actual = createAndInitClasspath(); + assertThat(actual.elements()).isEmpty(); + assertThat(actual.projectWarnings()).isEmpty(); + assertThat(actual.logs()).containsExactly( + "DEBUG Property 'sonar.java.jdkHome' resolved with: []", + "DEBUG Property 'sonar.java.binaries' resolved with: []", + "DEBUG Property 'sonar.java.libraries' resolved with: []" + ); } @Test - void no_warning_for_missing_bytecode_when_libraries_empty_and_have_no_java_sources() { - javaClasspath = new ClasspathForMain(settings.asConfig(), new DefaultFileSystem(new File("src/test/files/classpath/"))); - javaClasspath.init(); - assertThat(javaClasspath.getFilesFromProperty(ClasspathProperties.SONAR_JAVA_LIBRARIES)).isEmpty(); - assertThat(javaClasspath.hasJavaSources()).isFalse(); + void when_binaries_property_not_defined_classpath_does_not_log_a_warning_if_only_one_java_file() { + settings.removeProperty(ClasspathProperties.SONAR_JAVA_BINARIES); + ClassPathResult actual = createAndInitClasspath(); + assertThat(actual.elements()).containsExactly("/empty"); + assertThat(actual.projectWarnings()).isEmpty(); + assertThat(actual.logs()).containsExactly( + "DEBUG Property 'sonar.java.jdkHome' resolved with: []", + "DEBUG Property 'sonar.java.binaries' resolved with: []", + "DEBUG Property 'sonar.java.libraries' resolved with: [/empty]" + ); + } - javaClasspath.logSuspiciousEmptyLibraries(); + @Test + void when_binaries_property_not_defined_classpath_does_not_log_a_warning_if_no_java_file() { + settings.removeProperty(ClasspathProperties.SONAR_JAVA_BINARIES); + fs = new DefaultFileSystem(new File("src/test/files/classpath/")); + ClassPathResult actual = createAndInitClasspath(); + assertThat(actual.elements()).containsExactly("/empty"); + assertThat(actual.projectWarnings()).isEmpty(); + assertThat(actual.logs()).containsExactly( + "DEBUG Property 'sonar.java.jdkHome' resolved with: []", + "DEBUG Property 'sonar.java.binaries' resolved with: []", + "DEBUG Property 'sonar.java.libraries' resolved with: [/empty]" + ); + } - assertThat(logTester.logs(Level.WARN)).isEmpty(); + @Test + void only_display_once_warning_for_missing_bytecode_when_libraries_empty_and_have_java_sources() { + settings.removeProperty(ClasspathProperties.SONAR_JAVA_BINARIES); + createTwoFilesInFileSystem(); + ClasspathForMain classpath = createJavaClasspath(); + classpath.getElements(); + classpath.getElements(); + classpath.logClasspathWarnings(); + classpath.logClasspathWarnings(); + classpath.logClasspathWarnings(); + String warning = "Missing 'sonar.java.binaries' property. You might end up with less precise analysis results."; + assertThat((logTester.logs(Level.WARN))).containsExactly(warning); + assertThat(analysisWarningsLogged).containsExactly(warning); } @Test void do_not_register_warning_for_missing_bytecode_when_wrapper_not_injected() { - javaClasspath = new ClasspathForMain(settings.asConfig(), fs); - javaClasspath.init(); - assertThat(javaClasspath.getFilesFromProperty(ClasspathProperties.SONAR_JAVA_LIBRARIES)).isEmpty(); - assertThat(javaClasspath.hasJavaSources()).isTrue(); - verifyNoInteractions(analysisWarnings); + settings.removeProperty(ClasspathProperties.SONAR_JAVA_BINARIES); + createTwoFilesInFileSystem(); + var javaClasspath = new ClasspathForMain(settings.asConfig(), fs); + javaClasspath.getElements(); + javaClasspath.logClasspathWarnings(); + String warning = "Missing 'sonar.java.binaries' property. You might end up with less precise analysis results."; + assertThat((logTester.logs(Level.WARN))).containsExactly(warning); + assertThat(analysisWarningsLogged).isEmpty(); } @Test void setting_binary_prop_should_fill_elements() { settings.setProperty(ClasspathProperties.SONAR_JAVA_BINARIES, "bin"); - javaClasspath = createJavaClasspath(); - assertThat(javaClasspath.getBinaryDirs()).hasSize(1); - assertThat(javaClasspath.getElements()).hasSize(1); - assertThat(javaClasspath.getElements().get(0)).exists(); + settings.removeProperty(ClasspathProperties.SONAR_JAVA_LIBRARIES); + createTwoFilesInFileSystem(); + ClassPathResult actual = createAndInitClasspath(); + assertThat(actual.classpath().getBinaryDirs()).hasSize(1); + assertThat(actual.elements()).containsExactly("/bin"); + assertThat(actual.classpath().getElements().get(0)).exists(); } @Test void setting_binary_dir_prop_should_fill_elements() { settings.setProperty(ClasspathProperties.SONAR_JAVA_BINARIES, "bin/"); - javaClasspath = createJavaClasspath(); - assertThat(javaClasspath.getBinaryDirs()).hasSize(1); - assertThat(javaClasspath.getElements()).hasSize(1); - assertThat(javaClasspath.getElements().get(0)).exists(); + settings.removeProperty(ClasspathProperties.SONAR_JAVA_LIBRARIES); + createTwoFilesInFileSystem(); + ClassPathResult actual = createAndInitClasspath(); + assertThat(actual.classpath().getBinaryDirs()).hasSize(1); + assertThat(actual.elements()).containsExactly("/bin"); + assertThat(actual.classpath().getElements().get(0)).exists(); } @Test void setting_library_prop_should_fill_elements() { settings.setProperty(ClasspathProperties.SONAR_JAVA_LIBRARIES, "lib/hello.jar"); - javaClasspath = createJavaClasspath(); - assertThat(javaClasspath.getBinaryDirs()).isEmpty(); - assertThat(javaClasspath.getElements()).hasSize(1); - assertThat(javaClasspath.getElements().get(0)).exists(); + settings.removeProperty(ClasspathProperties.SONAR_JAVA_BINARIES); + createTwoFilesInFileSystem(); + ClassPathResult actual = createAndInitClasspath(); + assertThat(actual.classpath().getBinaryDirs()).isEmpty(); + assertThat(actual.elements()).containsExactly("/lib/hello.jar"); + assertThat(actual.classpath().getElements().get(0)).exists(); } @Test void absolute_file_name_should_be_resolved() { - settings.setProperty(ClasspathProperties.SONAR_JAVA_LIBRARIES, new File("src/test/files/classpath/lib/hello.jar").getAbsolutePath()); - javaClasspath = createJavaClasspath(); - assertThat(javaClasspath.getElements()).hasSize(1); - assertThat(javaClasspath.getElements().get(0)).exists(); + var absolutePath = new File("src/test/files/classpath/lib/hello.jar").getAbsolutePath(); + settings.setProperty(ClasspathProperties.SONAR_JAVA_LIBRARIES, absolutePath); + settings.removeProperty(ClasspathProperties.SONAR_JAVA_BINARIES); + createTwoFilesInFileSystem(); + ClassPathResult actual = createAndInitClasspath(); + assertThat(actual.classpath().getBinaryDirs()).isEmpty(); + assertThat(actual.elements()).containsExactly("/lib/hello.jar"); + assertThat(actual.classpath().getElements().get(0)).exists(); } @Test void absolute_aar_file_name_should_be_resolved() { - settings.setProperty(ClasspathProperties.SONAR_JAVA_LIBRARIES, new File("src/test/files/classpath/lib/oklog-1.0.1.aar").getAbsolutePath()); - javaClasspath = createJavaClasspath(); - assertThat(javaClasspath.getElements()).hasSize(1); - assertThat(javaClasspath.getElements().get(0)).exists(); + var absolutePath = new File("src/test/files/classpath/lib/oklog-1.0.1.aar").getAbsolutePath(); + settings.setProperty(ClasspathProperties.SONAR_JAVA_LIBRARIES, absolutePath); + settings.removeProperty(ClasspathProperties.SONAR_JAVA_BINARIES); + createTwoFilesInFileSystem(); + ClassPathResult actual = createAndInitClasspath(); + assertThat(actual.classpath().getBinaryDirs()).isEmpty(); + assertThat(actual.elements()).containsExactly("/lib/oklog-1.0.1.aar"); + assertThat(actual.classpath().getElements().get(0)).exists(); } @Test void directory_specified_for_library_should_find_jars() { settings.setProperty(ClasspathProperties.SONAR_JAVA_LIBRARIES, "lib"); - javaClasspath = createJavaClasspath(); - assertThat(javaClasspath.getElements()).hasSize(4); - assertThat(javaClasspath.getElements().get(0)).exists(); - assertThat(javaClasspath.getElements().get(1)).exists(); - assertThat(javaClasspath.getElements().get(2)).exists(); - assertThat(javaClasspath.getElements().get(3)).exists(); - assertThat(javaClasspath.getElements()).extracting("name").contains("lib", "hello.jar", "world.jar", "foo.jar"); + settings.removeProperty(ClasspathProperties.SONAR_JAVA_BINARIES); + createTwoFilesInFileSystem(); + ClassPathResult actual = createAndInitClasspath(); + assertThat(actual.elements()).containsExactlyInAnyOrder( + "/lib/world.jar", + "/lib/target/classes/foo.jar", + "/lib/hello.jar", + "/lib" + ); + assertThat(actual.classpath().getElements().get(0)).exists(); + assertThat(actual.classpath().getElements().get(1)).exists(); + assertThat(actual.classpath().getElements().get(2)).exists(); + assertThat(actual.classpath().getElements().get(3)).exists(); } @Test void libraries_should_accept_path_ending_with_wildcard() { settings.setProperty(ClasspathProperties.SONAR_JAVA_LIBRARIES, "lib/*"); - javaClasspath = createJavaClasspath(); - assertThat(javaClasspath.getElements()).hasSize(3); - assertThat(javaClasspath.getElements().get(0)).exists(); - assertThat(javaClasspath.getElements().get(1)).exists(); - assertThat(javaClasspath.getElements().get(2)).exists(); - assertThat(javaClasspath.getElements()).extracting("name").contains("hello.jar", "world.jar", "target"); + settings.removeProperty(ClasspathProperties.SONAR_JAVA_BINARIES); + createTwoFilesInFileSystem(); + ClassPathResult actual = createAndInitClasspath(); + assertThat(actual.elements()).containsExactlyInAnyOrder( + "/lib/target", "/lib/world.jar", "/lib/hello.jar" + ); + assertThat(actual.classpath().getElements().get(0)).exists(); + assertThat(actual.classpath().getElements().get(1)).exists(); + assertThat(actual.classpath().getElements().get(2)).exists(); } @Test void libraries_should_keep_order() { settings.setProperty(ClasspathProperties.SONAR_JAVA_LIBRARIES, "lib/world.jar,lib/hello.jar,lib/target/classes/*"); - javaClasspath = createJavaClasspath(); - assertThat(javaClasspath.getElements()).hasSize(3); - assertThat(javaClasspath.getElements().get(0)).exists(); - assertThat(javaClasspath.getElements().get(1)).exists(); - assertThat(javaClasspath.getElements().get(2)).exists(); - assertThat(javaClasspath.getElements()).extracting("name").containsExactly("world.jar", "hello.jar", "foo.jar"); + settings.removeProperty(ClasspathProperties.SONAR_JAVA_BINARIES); + createTwoFilesInFileSystem(); + ClassPathResult actual = createAndInitClasspath(); + assertThat(actual.elements()).containsExactly( + "/lib/world.jar", + "/lib/hello.jar", + "/lib/target/classes/foo.jar" + ); + assertThat(actual.classpath().getElements().get(0)).exists(); + assertThat(actual.classpath().getElements().get(1)).exists(); + assertThat(actual.classpath().getElements().get(2)).exists(); } @Test void libraries_should_accept_relative_paths() { settings.setProperty(ClasspathProperties.SONAR_JAVA_LIBRARIES, "../../files/classpath/lib/*.jar"); - javaClasspath = createJavaClasspath(); - assertThat(javaClasspath.getElements()).hasSize(2); - assertThat(javaClasspath.getElements().get(0)).exists(); - assertThat(javaClasspath.getElements().get(1)).exists(); - assertThat(javaClasspath.getElements()).extracting("name").contains("hello.jar", "world.jar"); + settings.removeProperty(ClasspathProperties.SONAR_JAVA_BINARIES); + createTwoFilesInFileSystem(); + ClassPathResult actual = createAndInitClasspath(); + assertThat(actual.elements()).containsExactlyInAnyOrder( + "/lib/world.jar", + "/lib/hello.jar" + ); + assertThat(actual.classpath().getElements().get(0)).exists(); + assertThat(actual.classpath().getElements().get(1)).exists(); } @Test void libraries_should_accept_relative_paths_with_wildcard() { settings.setProperty(ClasspathProperties.SONAR_JAVA_LIBRARIES, "../../files/**/lib"); - javaClasspath = createJavaClasspath(); - assertThat(javaClasspath.getElements()).hasSize(9); - File jar = javaClasspath.getElements().get(0); - assertThat(jar).exists(); - assertThat(javaClasspath.getElements()).extracting("name").containsExactlyInAnyOrder( - "hello.jar", - "hello.jar", - "world.jar", - "emptyFile.jar", - "likeJdkJar.jar", - "emptyArchive.jar", - "lib", - "lib", - "oklog-1.0.1.aar"); + settings.removeProperty(ClasspathProperties.SONAR_JAVA_BINARIES); + createTwoFilesInFileSystem(); + ClassPathResult actual = createAndInitClasspath(); + assertThat(actual.elements()).containsExactlyInAnyOrder( + "/lib/world.jar", + "/lib/hello.jar", + "/lib/oklog-1.0.1.aar", + "src/test/files/other/lib/likeJdkJar.jar", + "src/test/files/other/lib/emptyArchive.jar", + "src/test/files/other/lib/hello.jar", + "src/test/files/other/lib/emptyFile.jar", + "/lib", + "src/test/files/other/lib" + ); } @Test void should_not_scan_target_classes() { settings.setProperty(ClasspathProperties.SONAR_JAVA_LIBRARIES, "../../files/classpath/lib/target/classes"); - javaClasspath = createJavaClasspath(); - assertThat(javaClasspath.getElements()).hasSize(1); - File classes = javaClasspath.getElements().get(0); - assertThat(classes).exists(); - assertThat(javaClasspath.getElements()).extracting("name").contains("classes"); + settings.removeProperty(ClasspathProperties.SONAR_JAVA_BINARIES); + createTwoFilesInFileSystem(); + ClassPathResult actual = createAndInitClasspath(); + assertThat(actual.elements()).containsExactly("/lib/target/classes"); } + @Test void libraries_should_accept_path_ending_with_wildcard_jar() { settings.setProperty(ClasspathProperties.SONAR_JAVA_LIBRARIES, "lib/h*.jar"); - javaClasspath = createJavaClasspath(); - assertThat(javaClasspath.getElements()).hasSize(1); - File jar = javaClasspath.getElements().get(0); - assertThat(jar) - .exists() - .hasName("hello.jar"); - - settings.setProperty(ClasspathProperties.SONAR_JAVA_LIBRARIES, "lib/*.jar"); - javaClasspath = createJavaClasspath(); - assertThat(javaClasspath.getElements()).hasSize(2); - jar = javaClasspath.getElements().get(0); - assertThat(jar).exists(); - assertThat(javaClasspath.getElements()).extracting("name").contains("hello.jar", "world.jar"); - + settings.removeProperty(ClasspathProperties.SONAR_JAVA_BINARIES); + createTwoFilesInFileSystem(); + ClassPathResult actual = createAndInitClasspath(); + assertThat(actual.elements()).containsExactly("/lib/hello.jar"); } @Test void directory_wildcard_should_be_resolved() { settings.setProperty(ClasspathProperties.SONAR_JAVA_LIBRARIES, "**/*.jar"); - javaClasspath = createJavaClasspath(); - assertThat(javaClasspath.getElements()).hasSize(4); - File jar = javaClasspath.getElements().get(0); - assertThat(jar).exists(); - assertThat(javaClasspath.getElements()).extracting("name").contains("hello.jar", "world.jar", "foo.jar", "android.jar"); + settings.removeProperty(ClasspathProperties.SONAR_JAVA_BINARIES); + createTwoFilesInFileSystem(); + ClassPathResult actual = createAndInitClasspath(); + assertThat(actual.elements()).containsExactlyInAnyOrder( + "/android/android.jar", + "/lib/world.jar", + "/lib/target/classes/foo.jar", + "/lib/hello.jar" + ); } @Test void wildcard_directory_should_resolve_libs_in_that_dir() { - logTester.setLevel(Level.DEBUG); settings.setProperty(ClasspathProperties.SONAR_JAVA_LIBRARIES, "lib/**/*.jar"); - javaClasspath = createJavaClasspath(); - assertThat(javaClasspath.getElements()).hasSize(3); - File jar = javaClasspath.getElements().get(0); - assertThat(jar).exists(); - assertThat(javaClasspath.getElements()).extracting("name").contains("hello.jar", "world.jar", "foo.jar"); - - assertThat(logTester.logs(Level.DEBUG)) - .hasSize(2) - .allMatch(debug -> (debug.startsWith("Property 'sonar.java.libraries' resolved with:") && debug.contains("world.jar") && debug.contains("hello.jar")) - || debug.equals("Property 'sonar.java.jdkHome' resolved with:" + System.lineSeparator() + "[]")); + settings.removeProperty(ClasspathProperties.SONAR_JAVA_BINARIES); + createTwoFilesInFileSystem(); + ClassPathResult actual = createAndInitClasspath(); + assertThat(actual.elements()).containsExactlyInAnyOrder( + "/lib/target/classes/foo.jar", + "/lib/world.jar", + "/lib/hello.jar" + ); } @Test - void both_path_separator_should_be_supported_on_one_JVM() { - settings.setProperty(ClasspathProperties.SONAR_JAVA_LIBRARIES, "**/*.jar"); - javaClasspath = createJavaClasspath(); - assertThat(javaClasspath.getElements()).hasSize(4); - File jar = javaClasspath.getElements().get(0); - assertThat(jar).exists(); - assertThat(javaClasspath.getElements()).extracting("name").contains("hello.jar", "world.jar", "foo.jar", "android.jar"); + void windows_path_separator_should_be_supported() { settings.setProperty(ClasspathProperties.SONAR_JAVA_LIBRARIES, "**\\*.jar"); - javaClasspath = createJavaClasspath(); - assertThat(javaClasspath.getElements()).hasSize(4); - jar = javaClasspath.getElements().get(0); - assertThat(jar).exists(); - assertThat(javaClasspath.getElements()).extracting("name").contains("hello.jar", "world.jar", "foo.jar", "android.jar"); + settings.removeProperty(ClasspathProperties.SONAR_JAVA_BINARIES); + createTwoFilesInFileSystem(); + ClassPathResult actual = createAndInitClasspath(); + assertThat(actual.elements()).containsExactlyInAnyOrder( + "/android/android.jar", + "/lib/world.jar", + "/lib/target/classes/foo.jar", + "/lib/hello.jar" + ); } @Test - void non_existing_resources_should_fail() { + void non_existing_libraries_should_log_a_warning() { settings.setProperty(ClasspathProperties.SONAR_JAVA_LIBRARIES, "toto/**/hello.jar"); - checkIllegalStateException("No files nor directories matching 'toto/**/hello.jar'"); - } - - @Test - void deprecated_properties_set_should_fail_the_analysis() { + settings.removeProperty(ClasspathProperties.SONAR_JAVA_BINARIES); + // only one file, no 'sonar.java.binaries' warning + ClassPathResult actual = createAndInitClasspath(); + assertThat(actual.elements()).isEmpty(); + String warning = "Invalid value for 'sonar.java.libraries', no files nor directories matching 'toto/**/hello.jar'."; + assertThat(actual.projectWarnings()).containsExactly(warning); + assertThat(actual.logs()).containsExactly( + "DEBUG Property 'sonar.java.jdkHome' resolved with: []", + "DEBUG Property 'sonar.java.binaries' resolved with: []", + "DEBUG Property 'sonar.java.libraries' resolved with: []", + "WARN " + warning + ); + } + + @Test + void deprecated_properties_are_not_supported_anymore() { + settings.removeProperty(ClasspathProperties.SONAR_JAVA_BINARIES); + settings.removeProperty(ClasspathProperties.SONAR_JAVA_LIBRARIES); settings.setProperty("sonar.binaries", "bin"); settings.setProperty("sonar.libraries", "hello.jar"); - javaClasspath = createJavaClasspath(); - assertThatThrownBy(javaClasspath::getElements) - .isInstanceOf(AnalysisException.class) - .hasMessage( - "sonar.binaries and sonar.libraries are not supported since version 4.0 of the SonarSource Java Analyzer, please use sonar.java.binaries and sonar.java.libraries instead" - ); - } - - @Test - void libraries_should_read_dir_of_class_files() { - fs = new DefaultFileSystem(new File("src/test/files/")); - fs.add(TestUtils.emptyInputFile("foo.java")); - settings.setProperty(ClasspathProperties.SONAR_JAVA_LIBRARIES, "classpath"); - javaClasspath = createJavaClasspath(); - assertThat(javaClasspath.getElements()).hasSize(5); - settings.setProperty(ClasspathProperties.SONAR_JAVA_LIBRARIES, "classpath/"); - javaClasspath = createJavaClasspath(); - assertThat(javaClasspath.getElements()).hasSize(5); + createTwoFilesInFileSystem(); + ClassPathResult actual = createAndInitClasspath(); + assertThat(actual.elements()).isEmpty(); + String warning = "Missing 'sonar.java.binaries' and 'sonar.java.libraries' properties. You might end up with less precise analysis results."; + assertThat(actual.projectWarnings()).containsExactly(warning); + assertThat(actual.logs()).containsExactly( + "DEBUG Property 'sonar.java.jdkHome' resolved with: []", + "DEBUG Property 'sonar.java.binaries' resolved with: []", + "DEBUG Property 'sonar.java.libraries' resolved with: []", + "WARN " + warning + ); } @Test void parent_module_should_not_validate_sonar_libraries() { settings.setProperty(ClasspathProperties.SONAR_JAVA_LIBRARIES, "non-existing.jar"); - javaClasspath = createJavaClasspath(); - checkIllegalStateException("No files nor directories matching 'non-existing.jar'"); - } - - @Test - void sonar_binaries_should_not_check_for_existence_of_files_when_no_sources() { - settings.setProperty(ClasspathProperties.SONAR_JAVA_BINARIES, "toto/**/hello.jar"); - fs = new DefaultFileSystem(new File("src/test/files/classpath/")); - fs.add(TestUtils.emptyInputFile("plop.java", InputFile.Type.TEST)); - javaClasspath = createJavaClasspath(); - assertThat(javaClasspath.getElements()).isEmpty(); - } - - @Test - void empty_binaries_on_project_with_more_than_one_source_should_fail() { - createTwoFilesInFileSystem(); - javaClasspath = createJavaClasspath(); - assertThatThrownBy(javaClasspath::getElements) - .isInstanceOf(AnalysisException.class) - .hasMessage( - "Your project contains .java files, please provide compiled classes with sonar.java.binaries property," - + " or exclude them from the analysis with sonar.exclusions property." - ); - } - - @Test - void empty_binaries_on_project_with_more_than_one_source_should_fail_on_sonarqube() { - createTwoFilesInFileSystem(); - javaClasspath = createJavaClasspath(); - - assertThatThrownBy(javaClasspath::getElements) - .isInstanceOf(AnalysisException.class) - .hasMessage( - "Your project contains .java files, please provide compiled classes with sonar.java.binaries property," - + " or exclude them from the analysis with sonar.exclusions property." - ); - } - - @Test - void empty_binaries_on_project_with_more_than_one_source_should_not_fail_on_sonarlint() { - createTwoFilesInFileSystem(); - - assertThatCode(() -> { - javaClasspath = new ClasspathForMainForSonarLint(settings.asConfig(), fs); - javaClasspath.getElements(); - }) - .withFailMessage("Analysis exception was raised but analysis should not fail") - .doesNotThrowAnyException(); - - assertThat(logTester.logs(Level.WARN)) - .contains("sonar.java.binaries is empty, please double check your configuration"); + settings.removeProperty(ClasspathProperties.SONAR_JAVA_BINARIES); + ClassPathResult actual = createAndInitClasspath(); + assertThat(actual.elements()).isEmpty(); + String warning = "Invalid value for 'sonar.java.libraries', no files nor directories matching 'non-existing.jar'."; + assertThat(actual.projectWarnings()).containsExactly(warning); + assertThat(actual.logs()).containsExactly( + "DEBUG Property 'sonar.java.jdkHome' resolved with: []", + "DEBUG Property 'sonar.java.binaries' resolved with: []", + "DEBUG Property 'sonar.java.libraries' resolved with: []", + "WARN " + warning + ); } private void createTwoFilesInFileSystem() { @@ -425,58 +481,88 @@ private void createTwoFilesInFileSystem() { @Test void classpath_empty_if_only_test_files() { + settings.removeProperty(ClasspathProperties.SONAR_JAVA_BINARIES); + settings.removeProperty(ClasspathProperties.SONAR_JAVA_LIBRARIES); fs = new DefaultFileSystem(new File("src/test/files/classpath/")); fs.add(TestUtils.emptyInputFile("plop.java", InputFile.Type.TEST)); - javaClasspath = createJavaClasspath(); - assertThat(javaClasspath.getElements()).isEmpty(); + ClassPathResult actual = createAndInitClasspath(); + assertThat(actual.elements()).isEmpty(); } @Test void validate_libraries_only_if_not_filtered_out() { + settings.removeProperty(ClasspathProperties.SONAR_JAVA_BINARIES); + settings.removeProperty(ClasspathProperties.SONAR_JAVA_LIBRARIES); settings.setProperty(ClasspathProperties.SONAR_JAVA_LIBRARIES, new File("src/test/files/classpath/lib/lib.so").getAbsolutePath()); fs = new DefaultFileSystem(new File("src/test/files/classpath/")); fs.add(TestUtils.emptyInputFile("plop.java")); - javaClasspath = createJavaClasspath(); - assertThat(javaClasspath.getElements()).isEmpty(); + ClassPathResult actual = createAndInitClasspath(); + assertThat(actual.elements()).isEmpty(); } @Test - void invalid_sonar_java_binaries_should_fail_analysis() { + void invalid_sonar_java_binaries_should_fail_log_a_warning() { settings.setProperty(ClasspathProperties.SONAR_JAVA_BINARIES, "dummyDir"); - checkIllegalStateException("No files nor directories matching 'dummyDir'"); + createTwoFilesInFileSystem(); + ClassPathResult actual = createAndInitClasspath(); + assertThat(actual.elements()).containsExactly("/empty"); + String warning = "Invalid value for 'sonar.java.binaries', no files nor directories matching 'dummyDir'."; + assertThat(actual.projectWarnings()).containsExactly(warning); + assertThat(actual.logs()).containsExactly( + "DEBUG Property 'sonar.java.jdkHome' resolved with: []", + "DEBUG Property 'sonar.java.binaries' resolved with: []", + "DEBUG Property 'sonar.java.libraries' resolved with: [/empty]", + "WARN " + warning + ); } @ParameterizedTest @ValueSource(booleans = {true, false}) void by_default_no_jdk_is_set(boolean debugEnabled) { logTester.setLevel(debugEnabled ? Level.DEBUG : Level.INFO); - List elements = createJavaClasspath().getElements(); - - assertThat(elements).isEmpty(); - List logs = logTester.logs(Level.DEBUG); + ClassPathResult actual = createAndInitClasspath(); + assertThat(actual.projectWarnings()).isEmpty(); if (debugEnabled) { - assertThat(logs).containsExactlyInAnyOrder( - "Property 'sonar.java.jdkHome' resolved with:" + System.lineSeparator() + "[]", - "Property 'sonar.java.libraries' resolved with:" + System.lineSeparator() + "[]"); + assertThat(actual.logs()).containsExactly( + "DEBUG Property 'sonar.java.jdkHome' resolved with: []", + "DEBUG Property 'sonar.java.binaries' resolved with: [/empty]", + "DEBUG Property 'sonar.java.libraries' resolved with: [/empty]" + ); } else { - assertThat(logs).isEmpty(); + assertThat(actual.logs()).isEmpty(); } } - @ParameterizedTest - @ValueSource(strings = {"src/test/jdk/do-not-exists", "src/test/jdk/README.txt"}) - void wrong_sdk_path_does_not_make_classpath_init_fail(String path) { - settings.setProperty(ClasspathProperties.SONAR_JAVA_JDK_HOME, path); - String expectedWarning = String.format( - "Invalid value for 'sonar.java.jdkHome' property, defaulting to runtime JDK.%sConfigured location does not exists:", - System.lineSeparator()); - - List elements = createJavaClasspath().getElements(); + @Test + void sdk_not_found_path_does_not_make_classpath_init_fail() { + settings.setProperty(ClasspathProperties.SONAR_JAVA_JDK_HOME, "src/test/jdk/do-not-exists"); + ClassPathResult actual = createAndInitClasspath(); + assertThat(actual.elements()).containsExactly("/empty"); + String warning = "Invalid value '/do-not-exists' for 'sonar.java.jdkHome' property, defaulting to runtime JDK."; + assertThat(actual.projectWarnings()).containsExactly(warning); + assertThat(actual.logs()).containsExactly( + "DEBUG Property 'sonar.java.jdkHome' set with: /do-not-exists", + "DEBUG Property 'sonar.java.jdkHome' resolved with: []", + "DEBUG Property 'sonar.java.binaries' resolved with: [/empty]", + "DEBUG Property 'sonar.java.libraries' resolved with: [/empty]", + "WARN " + warning + ); + } - assertThat(elements).isEmpty(); - assertThat(logTester.logs(Level.WARN).stream()) - .filteredOn(warn -> warn.startsWith(expectedWarning)) - .hasSize(1); + @Test + void invalid_sdk_path_does_not_make_classpath_init_fail() { + settings.setProperty(ClasspathProperties.SONAR_JAVA_JDK_HOME, "src/test/jdk/README.txt"); + ClassPathResult actual = createAndInitClasspath(); + assertThat(actual.elements()).containsExactly("/empty"); + String warning = "Invalid value '/README.txt' for 'sonar.java.jdkHome' property, defaulting to runtime JDK."; + assertThat(actual.projectWarnings()).containsExactly(warning); + assertThat(actual.logs()).containsExactly( + "DEBUG Property 'sonar.java.jdkHome' set with: /README.txt", + "DEBUG Property 'sonar.java.jdkHome' resolved with: []", + "DEBUG Property 'sonar.java.binaries' resolved with: [/empty]", + "DEBUG Property 'sonar.java.libraries' resolved with: [/empty]", + "WARN " + warning + ); } @ParameterizedTest @@ -485,23 +571,22 @@ void should_include_jdk_in_libraries_when_specified(String jdkFolder, String exp logTester.setLevel(Level.DEBUG); String pathToJdk = "src/test/jdk/" + jdkFolder; settings.setProperty(ClasspathProperties.SONAR_JAVA_JDK_HOME, pathToJdk); - - List elements = createJavaClasspath().getElements(); - - assertThat(elements) - .hasSize(1) - .allMatch(file -> file.getName().equals(expectedJar)); - assertThat(logTester.logs(Level.DEBUG)) - .hasSize(3) - .allMatch(debug -> (debug.startsWith("Property 'sonar.java.jdkHome' set with:") && debug.contains(jdkFolder)) - || (debug.startsWith("Property 'sonar.java.jdkHome' resolved with:") && debug.contains(expectedJar)) - || (debug.equals("Property 'sonar.java.libraries' resolved with:" + System.lineSeparator() + "[]"))); + ClassPathResult actual = createAndInitClasspath(); + String libFolder = jdkFolder.equals("jdk_classic") ? "/jdk_classic/jre/lib/" : "/jdk_modular/lib/"; + assertThat(actual.elements()).containsExactly("/empty", libFolder + expectedJar); + assertThat(actual.projectWarnings()).isEmpty(); + assertThat(actual.logs()).containsExactly( + "DEBUG Property 'sonar.java.jdkHome' set with: /" + jdkFolder, + "DEBUG Property 'sonar.java.jdkHome' resolved with: [" + libFolder + expectedJar + "]", + "DEBUG Property 'sonar.java.binaries' resolved with: [/empty]", + "DEBUG Property 'sonar.java.libraries' resolved with: [" + libFolder + expectedJar + ",/empty]" + ); } @Test void should_not_be_in_android_context_by_default() { settings.setProperty(ClasspathProperties.SONAR_JAVA_LIBRARIES, "lib/hello.jar"); - javaClasspath = createJavaClasspath(); + var javaClasspath = createJavaClasspath(); javaClasspath.init(); assertThat(javaClasspath.inAndroidContext()).isFalse(); } @@ -509,7 +594,7 @@ void should_not_be_in_android_context_by_default() { @Test void should_set_in_android_context() { settings.setProperty(ClasspathProperties.SONAR_JAVA_LIBRARIES, "android/android.jar"); - javaClasspath = createJavaClasspath(); + var javaClasspath = createJavaClasspath(); javaClasspath.init(); assertThat(javaClasspath.inAndroidContext()).isTrue(); } @@ -517,7 +602,7 @@ void should_set_in_android_context() { @Test void should_set_in_android_context_indirect() { settings.setProperty(ClasspathProperties.SONAR_JAVA_LIBRARIES, "android/*.jar"); - javaClasspath = createJavaClasspath(); + var javaClasspath = createJavaClasspath(); javaClasspath.init(); assertThat(javaClasspath.inAndroidContext()).isTrue(); } @@ -525,22 +610,61 @@ void should_set_in_android_context_indirect() { @Test void libraries_should_accept_paths_with_comma_csv_escaped() { settings.setProperty(ClasspathProperties.SONAR_JAVA_LIBRARIES, "lib/hello.jar,\"../classpath_with_comma/hello,world.jar\""); - javaClasspath = createJavaClasspath(); - assertThat(javaClasspath.getElements()).hasSize(2); - assertThat(javaClasspath.getElements().get(0)).exists(); - assertThat(javaClasspath.getElements().get(1)).exists(); - assertThat(javaClasspath.getElements()).extracting("name").contains("hello.jar", "hello,world.jar"); + settings.removeProperty(ClasspathProperties.SONAR_JAVA_BINARIES); + createTwoFilesInFileSystem(); + ClassPathResult actual = createAndInitClasspath(); + assertThat(actual.elements()).containsExactly( + "/lib/hello.jar", + "src/test/files/classpath_with_comma/hello,world.jar" + ); } - private void checkIllegalStateException(String message) { - javaClasspath = createJavaClasspath(); + private ClasspathForMain createJavaClasspath() { + return new ClasspathForMain(settings.asConfig(), fs, analysisWarnings); + } - assertThatThrownBy(javaClasspath::getElements) - .isInstanceOf(IllegalStateException.class) - .hasMessage(message); + private ClassPathResult createAndInitClasspath() { + ClasspathForMain classpath = createJavaClasspath(); + List elements = classpath.getElements().stream().map(File::getPath).map(this::replaceProjectPath).toList(); + classpath.logClasspathWarnings(); + List logs = new ArrayList<>(); + for(var level : List.of(Level.DEBUG, Level.INFO, Level.WARN, Level.ERROR)) { + logTester.logs(level).forEach(log -> logs.add(level + " " + replaceProjectPath(log))); + } + return new ClassPathResult(classpath, + elements, + logs, + replaceProjectPath(analysisWarningsLogged) + ); } - private ClasspathForMain createJavaClasspath() { - return new ClasspathForMain(settings.asConfig(), fs, analysisWarnings); + record ClassPathResult( + ClasspathForMain classpath, + List elements, + List logs, + List projectWarnings + ) { + } + + private String replaceProjectPath(String text) { + return normalizePath(text) + .replace(normalizePath(baseDir) + "/", "/") + .replace(normalizePath(RELATIVE_BASE_PATH) + "/", "/") + .replace(normalizePath(jdkPath) + "/", "/") + .replace(normalizePath(RELATIVE_JDK_PATH) + "/", "/"); + + } + + private static String normalizePath(Path path) { + return normalizePath(path.toString()); + } + + private static String normalizePath(String path) { + return path.replace('\\', '/'); } + + private List replaceProjectPath(List list) { + return list.stream().map(this::replaceProjectPath).toList(); + } + } diff --git a/java-frontend/src/test/java/org/sonar/java/classpath/ClasspathForTestTest.java b/java-frontend/src/test/java/org/sonar/java/classpath/ClasspathForTestTest.java index 3f8eb504c52..0aa5371d2b9 100644 --- a/java-frontend/src/test/java/org/sonar/java/classpath/ClasspathForTestTest.java +++ b/java-frontend/src/test/java/org/sonar/java/classpath/ClasspathForTestTest.java @@ -28,7 +28,6 @@ import org.sonar.java.testing.ThreadLocalLogTester; import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.fail; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verifyNoInteractions; @@ -65,12 +64,11 @@ void display_warning_for_missing_bytecode_when_libraries_empty_and_have_java_sou javaTestClasspath = createJavaClasspath(); javaTestClasspath.init(); assertThat(javaTestClasspath.getFilesFromProperty(ClasspathProperties.SONAR_JAVA_TEST_LIBRARIES)).isEmpty(); - assertThat(javaTestClasspath.hasJavaSources()).isTrue(); + assertThat(javaTestClasspath.hasJavaFiles()).isTrue(); - javaTestClasspath.logSuspiciousEmptyLibraries(); + javaTestClasspath.logClasspathWarnings(); - String warning = "Dependencies/libraries were not provided for analysis of TEST files. The 'sonar.java.test.libraries' property is empty. " - + "Verify your configuration, as you might end up with less precise results."; + String warning = "Missing 'sonar.java.test.libraries' property. You might end up with less precise analysis results."; assertThat(logTester.logs(Level.WARN)).containsExactly(warning); } @@ -79,15 +77,14 @@ void only_display_once_warning_for_missing_bytecode_when_libraries_empty_and_hav javaTestClasspath = createJavaClasspath(); javaTestClasspath.init(); assertThat(javaTestClasspath.getFilesFromProperty(ClasspathProperties.SONAR_JAVA_TEST_LIBRARIES)).isEmpty(); - assertThat(javaTestClasspath.hasJavaSources()).isTrue(); + assertThat(javaTestClasspath.hasJavaFiles()).isTrue(); - javaTestClasspath.logSuspiciousEmptyLibraries(); + javaTestClasspath.logClasspathWarnings(); //re-trigger logs - javaTestClasspath.logSuspiciousEmptyLibraries(); + javaTestClasspath.logClasspathWarnings(); - String warning = "Dependencies/libraries were not provided for analysis of TEST files. The 'sonar.java.test.libraries' property is empty. " - + "Verify your configuration, as you might end up with less precise results."; + String warning = "Missing 'sonar.java.test.libraries' property. You might end up with less precise analysis results."; assertThat(logTester.logs(Level.WARN)).containsExactly(warning); } @@ -96,9 +93,9 @@ void no_warning_for_missing_bytecode_when_libraries_empty_and_have_no_java_sourc javaTestClasspath = new ClasspathForTest(settings.asConfig(), new DefaultFileSystem(new File("src/test/files/classpath/"))); javaTestClasspath.init(); assertThat(javaTestClasspath.getFilesFromProperty(ClasspathProperties.SONAR_JAVA_TEST_LIBRARIES)).isEmpty(); - assertThat(javaTestClasspath.hasJavaSources()).isFalse(); + assertThat(javaTestClasspath.hasJavaFiles()).isFalse(); - javaTestClasspath.logSuspiciousEmptyLibraries(); + javaTestClasspath.logClasspathWarnings(); assertThat(logTester.logs(Level.WARN)).isEmpty(); } @@ -133,20 +130,13 @@ void empty_libraries_if_only_main_files() { void libraries_without_dir() { settings.setProperty(ClasspathProperties.SONAR_JAVA_TEST_BINARIES, "bin"); settings.setProperty(ClasspathProperties.SONAR_JAVA_TEST_LIBRARIES, "hello.jar"); - checkIllegalStateException("No files nor directories matching 'hello.jar'"); - } - - private void checkIllegalStateException(String message) { javaTestClasspath = createJavaClasspath(); - try { - javaTestClasspath.getElements(); - fail("Exception should have been raised"); - }catch (IllegalStateException ise) { - assertThat(ise.getMessage()).isEqualTo(message); - } + javaTestClasspath.getElements(); + javaTestClasspath.logClasspathWarnings(); + String warning = "Invalid value for 'sonar.java.test.libraries', no files nor directories matching 'hello.jar'."; + assertThat(logTester.logs(Level.WARN)).containsExactly(warning); } - private ClasspathForTest createJavaClasspath() { return new ClasspathForTest(settings.asConfig(), fs); } diff --git a/sonar-java-plugin/src/main/java/org/sonar/plugins/java/JavaPlugin.java b/sonar-java-plugin/src/main/java/org/sonar/plugins/java/JavaPlugin.java index f013d3e1f67..1f347cfef23 100644 --- a/sonar-java-plugin/src/main/java/org/sonar/plugins/java/JavaPlugin.java +++ b/sonar-java-plugin/src/main/java/org/sonar/plugins/java/JavaPlugin.java @@ -32,7 +32,6 @@ import org.sonar.java.JavaConstants; import org.sonar.java.SonarComponents; import org.sonar.java.classpath.ClasspathForMain; -import org.sonar.java.classpath.ClasspathForMainForSonarLint; import org.sonar.java.classpath.ClasspathForTest; import org.sonar.java.classpath.ClasspathProperties; import org.sonar.java.filters.PostAnalysisIssueFilter; @@ -52,7 +51,6 @@ public void define(Context context) { if (context.getRuntime().getProduct() == SonarProduct.SONARLINT) { list.add(NoOpTelemetry.class); - list.add(ClasspathForMainForSonarLint.class); // Some custom rules (i.e. DBD) depend on the presence of SonarLintCache when executing in a SonarLint context. // Hence, we must provide it here. list.add(SonarLintCache.class); @@ -63,7 +61,6 @@ public void define(Context context) { list.add(DroppedPropertiesSensor.class); list.add(JavaSonarWayProfile.class); list.add(JavaAgenticAIProfile.class); - list.add(ClasspathForMain.class); ExternalReportExtensions.define(context); } @@ -72,6 +69,7 @@ public void define(Context context) { } list.addAll(ClasspathProperties.getProperties()); list.addAll(Arrays.asList( + ClasspathForMain.class, ClasspathForTest.class, Java.class, PropertyDefinition.builder(Java.FILE_SUFFIXES_KEY)