]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-17610 Backend documentation links targets https://docs.sonarqube.org/ instead...
authorBenjamin Campomenosi <benjamin.campomenosi@sonarsource.com>
Tue, 20 Dec 2022 16:33:54 +0000 (17:33 +0100)
committersonartech <sonartech@sonarsource.com>
Wed, 28 Dec 2022 20:02:48 +0000 (20:02 +0000)
server/sonar-ce/src/main/java/org/sonar/ce/container/ComputeEngineContainerImpl.java
server/sonar-webserver/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java
sonar-core/src/main/java/org/sonar/core/documentation/DefaultDocumentationLinkGenerator.java [new file with mode: 0644]
sonar-core/src/main/java/org/sonar/core/documentation/DocumentationLinkGenerator.java [new file with mode: 0644]
sonar-core/src/test/java/org/sonar/core/documentation/DefaultDocumentationLinkGeneratorTest.java [new file with mode: 0644]
sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/SpringGlobalContainer.java
sonar-scanner-engine/src/main/java/org/sonar/scanner/scm/DefaultBlameOutput.java
sonar-scanner-engine/src/main/java/org/sonar/scanner/scm/ScmPublisher.java
sonar-scanner-engine/src/main/java/org/sonar/scm/git/GitScmProvider.java
sonar-scanner-engine/src/test/java/org/sonar/scanner/scm/DefaultBlameOutputTest.java
sonar-scanner-engine/src/test/java/org/sonar/scm/git/GitScmProviderTest.java

index 847bc10c2dcd1925901f2d3ce8a1a381cf553d17..1dd7307f102d88941688cb89420e5e961d172601 100644 (file)
@@ -66,6 +66,7 @@ import org.sonar.ce.taskprocessor.CeProcessingScheduler;
 import org.sonar.ce.taskprocessor.CeTaskProcessorModule;
 import org.sonar.core.component.DefaultResourceTypes;
 import org.sonar.core.config.CorePropertyDefinitions;
+import org.sonar.core.documentation.DefaultDocumentationLinkGenerator;
 import org.sonar.core.extension.CoreExtensionRepositoryImpl;
 import org.sonar.core.extension.CoreExtensionsLoader;
 import org.sonar.core.language.LanguagesProvider;
@@ -451,7 +452,9 @@ public class ComputeEngineContainerImpl implements ComputeEngineContainer {
       QualityGateFinder.class,
       QualityGateEvaluatorImpl.class,
 
-      new AnalysisCacheCleaningModule()
+      new AnalysisCacheCleaningModule(),
+
+      DefaultDocumentationLinkGenerator.class
 
     );
 
index a57f28666b7a04e551a6497a986eb83119668e58..587d180255aeb5b023270ba4ce3afc4e730ae6a3 100644 (file)
@@ -49,6 +49,7 @@ import org.sonar.ce.task.projectanalysis.taskprocessor.IssueSyncTaskProcessor;
 import org.sonar.ce.task.projectanalysis.taskprocessor.ReportTaskProcessor;
 import org.sonar.ce.task.projectexport.taskprocessor.ProjectExportTaskProcessor;
 import org.sonar.core.component.DefaultResourceTypes;
+import org.sonar.core.documentation.DefaultDocumentationLinkGenerator;
 import org.sonar.core.extension.CoreExtensionsInstaller;
 import org.sonar.core.language.LanguagesProvider;
 import org.sonar.core.platform.PlatformEditionProvider;
@@ -300,6 +301,7 @@ public class PlatformLevel4 extends PlatformLevel {
       WebAnalyticsLoaderImpl.class,
       new MonitoringWsModule(),
       DefaultBranchNameResolver.class,
+      DefaultDocumentationLinkGenerator.class,
 
       // batch
       new BatchWsModule(),
diff --git a/sonar-core/src/main/java/org/sonar/core/documentation/DefaultDocumentationLinkGenerator.java b/sonar-core/src/main/java/org/sonar/core/documentation/DefaultDocumentationLinkGenerator.java
new file mode 100644 (file)
index 0000000..148ef83
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.core.documentation;
+
+import java.util.Optional;
+import javax.annotation.Nullable;
+import org.sonar.api.utils.Version;
+import org.sonar.core.platform.SonarQubeVersion;
+
+public class DefaultDocumentationLinkGenerator implements DocumentationLinkGenerator {
+  private static final String DOCUMENTATION_PUBLIC_URL = "https://docs.sonarqube.org/";
+
+  private final String documentationBaseUrl;
+
+  public DefaultDocumentationLinkGenerator(SonarQubeVersion sonarQubeVersion) {
+    Version version = sonarQubeVersion.get();
+    this.documentationBaseUrl = DOCUMENTATION_PUBLIC_URL + version.major() + "." + version.minor();
+  }
+
+  @Override
+  public String getDocumentationLink(@Nullable String suffix) {
+    return documentationBaseUrl + Optional.ofNullable(suffix).orElse("");
+  }
+}
diff --git a/sonar-core/src/main/java/org/sonar/core/documentation/DocumentationLinkGenerator.java b/sonar-core/src/main/java/org/sonar/core/documentation/DocumentationLinkGenerator.java
new file mode 100644 (file)
index 0000000..fb262e2
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.core.documentation;
+
+import javax.annotation.Nullable;
+import org.sonar.api.ce.ComputeEngineSide;
+import org.sonar.api.scanner.ScannerSide;
+import org.sonar.api.server.ServerSide;
+
+@ServerSide
+@ComputeEngineSide
+@ScannerSide
+public interface DocumentationLinkGenerator {
+
+  String getDocumentationLink(@Nullable String suffix);
+
+}
diff --git a/sonar-core/src/test/java/org/sonar/core/documentation/DefaultDocumentationLinkGeneratorTest.java b/sonar-core/src/test/java/org/sonar/core/documentation/DefaultDocumentationLinkGeneratorTest.java
new file mode 100644 (file)
index 0000000..9926bb2
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.core.documentation;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Answers;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+import org.sonar.core.platform.SonarQubeVersion;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.when;
+
+@RunWith(MockitoJUnitRunner.class)
+public class DefaultDocumentationLinkGeneratorTest {
+  private static final String TEST_SUFFIX = "/documentation/analyzing-source-code/scm-integration/";
+
+  @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+  private SonarQubeVersion sonarQubeVersion;
+
+  private DefaultDocumentationLinkGenerator documentationLinkGenerator;
+
+  @Before
+  public void setUp() {
+    when(sonarQubeVersion.get().major()).thenReturn(100);
+    when(sonarQubeVersion.get().minor()).thenReturn(1000);
+    documentationLinkGenerator = new DefaultDocumentationLinkGenerator(sonarQubeVersion);
+  }
+
+  @Test
+  public void getDocumentationLink_whenSuffixProvided_concatenatesIt() {
+    String generatedLink = documentationLinkGenerator.getDocumentationLink(TEST_SUFFIX);
+
+    assertThat(generatedLink).isEqualTo("https://docs.sonarqube.org/100.1000/documentation/analyzing-source-code/scm-integration/");
+  }
+
+  @Test
+  public void getDocumentationLink_whenSuffixNotProvided_returnsBaseUrl() {
+    String generatedLink = documentationLinkGenerator.getDocumentationLink(null);
+
+    assertThat(generatedLink).isEqualTo("https://docs.sonarqube.org/100.1000");
+  }
+}
index 78be7024302095eee0b72d1444626648e92b39ca..02ec64c891cf426469c24e8b2c0ed02c73a70718 100644 (file)
@@ -36,6 +36,7 @@ import org.sonar.api.utils.UriReader;
 import org.sonar.api.utils.Version;
 import org.sonar.api.utils.log.Logger;
 import org.sonar.api.utils.log.Loggers;
+import org.sonar.core.documentation.DefaultDocumentationLinkGenerator;
 import org.sonar.core.extension.CoreExtensionRepositoryImpl;
 import org.sonar.core.extension.CoreExtensionsLoader;
 import org.sonar.core.platform.PluginClassLoader;
@@ -96,6 +97,7 @@ public class SpringGlobalContainer extends SpringComponentContainer {
       new GlobalConfigurationProvider(),
       new ScannerWsClientProvider(),
       DefaultServer.class,
+      DefaultDocumentationLinkGenerator.class,
       new GlobalTempFolderProvider(),
       analysisWarnings,
       UriReader.class,
index d7d512da5b19f6bfd1335dd25168f8600397a6af..d821f809047911a7be68ccfcd30ae96eb5fd75f1 100644 (file)
@@ -19,6 +19,7 @@
  */
 package org.sonar.scanner.scm;
 
+import com.google.common.annotations.VisibleForTesting;
 import java.util.HashMap;
 import java.util.LinkedHashSet;
 import java.util.List;
@@ -35,27 +36,34 @@ import org.sonar.api.batch.scm.BlameLine;
 import org.sonar.api.notifications.AnalysisWarnings;
 import org.sonar.api.utils.log.Logger;
 import org.sonar.api.utils.log.Loggers;
+import org.sonar.core.documentation.DocumentationLinkGenerator;
 import org.sonar.scanner.protocol.output.ScannerReport;
 import org.sonar.scanner.protocol.output.ScannerReport.Changesets.Builder;
 import org.sonar.scanner.protocol.output.ScannerReportWriter;
 import org.sonar.scanner.util.ProgressReport;
 
+import static java.lang.String.format;
 import static org.sonar.api.utils.Preconditions.checkArgument;
 
 class DefaultBlameOutput implements BlameOutput {
 
   private static final Logger LOG = Loggers.get(DefaultBlameOutput.class);
+  @VisibleForTesting
+  static final String SCM_INTEGRATION_DOCUMENTATION_SUFFIX = "/analyzing-source-code/scm-integration/";
 
   private final ScannerReportWriter writer;
   private AnalysisWarnings analysisWarnings;
+  private final DocumentationLinkGenerator documentationLinkGenerator;
   private final Set<InputFile> allFilesToBlame = new LinkedHashSet<>();
   private ProgressReport progressReport;
   private int count;
   private int total;
 
-  DefaultBlameOutput(ScannerReportWriter writer, AnalysisWarnings analysisWarnings, List<InputFile> filesToBlame) {
+  DefaultBlameOutput(ScannerReportWriter writer, AnalysisWarnings analysisWarnings, List<InputFile> filesToBlame,
+    DocumentationLinkGenerator documentationLinkGenerator) {
     this.writer = writer;
     this.analysisWarnings = analysisWarnings;
+    this.documentationLinkGenerator = documentationLinkGenerator;
     this.allFilesToBlame.addAll(filesToBlame);
     count = 0;
     total = filesToBlame.size();
@@ -134,12 +142,12 @@ class DefaultBlameOutput implements BlameOutput {
         LOG.warn("  * " + f);
       }
       LOG.warn("This may lead to missing/broken features in SonarQube");
-      String link = "/documentation/analyzing-source-code/scm-integration/";
-      analysisWarnings.addUnique(String.format("Missing blame information for %d %s. This may lead to some features not working correctly. " +
-        "Please check the analysis logs and refer to <a href=\"%s\" target=\"_blank\">the documentation</a>.",
+      String docUrl = documentationLinkGenerator.getDocumentationLink(SCM_INTEGRATION_DOCUMENTATION_SUFFIX);
+      analysisWarnings.addUnique(format("Missing blame information for %d %s. This may lead to some features not working correctly. " +
+        "Please check the analysis logs and refer to <a href=\"%s\" rel=\"noopener noreferrer\" target=\"_blank\">the documentation</a>.",
         allFilesToBlame.size(),
         allFilesToBlame.size() > 1 ? "files" : "file",
-        link));
+        docUrl));
     }
   }
 
@@ -147,3 +155,4 @@ class DefaultBlameOutput implements BlameOutput {
     return filesCount == 1 ? "source file" : "source files";
   }
 }
+
index e773c0d3bcfdaba161cf7f94ea676f308cc09f83..fd24e4b405233aff0f3c3cf3009f4480caed8454 100644 (file)
@@ -31,6 +31,7 @@ import org.sonar.api.batch.scm.ScmProvider;
 import org.sonar.api.notifications.AnalysisWarnings;
 import org.sonar.api.utils.log.Logger;
 import org.sonar.api.utils.log.Loggers;
+import org.sonar.core.documentation.DocumentationLinkGenerator;
 import org.sonar.scanner.protocol.output.ScannerReport;
 import org.sonar.scanner.protocol.output.ScannerReport.Changesets.Builder;
 import org.sonar.scanner.protocol.output.ScannerReportWriter;
@@ -49,11 +50,12 @@ public final class ScmPublisher {
   private final InputComponentStore componentStore;
   private final FileSystem fs;
   private final ScannerReportWriter writer;
-  private AnalysisWarnings analysisWarnings;
+  private final AnalysisWarnings analysisWarnings;
   private final BranchConfiguration branchConfiguration;
+  private final DocumentationLinkGenerator documentationLinkGenerator;
 
   public ScmPublisher(ScmConfiguration configuration, ProjectRepositories projectRepositories, InputComponentStore componentStore, FileSystem fs,
-    ReportPublisher reportPublisher, BranchConfiguration branchConfiguration, AnalysisWarnings analysisWarnings) {
+    ReportPublisher reportPublisher, BranchConfiguration branchConfiguration, AnalysisWarnings analysisWarnings, DocumentationLinkGenerator documentationLinkGenerator) {
     this.configuration = configuration;
     this.projectRepositories = projectRepositories;
     this.componentStore = componentStore;
@@ -61,6 +63,7 @@ public final class ScmPublisher {
     this.branchConfiguration = branchConfiguration;
     this.writer = reportPublisher.getWriter();
     this.analysisWarnings = analysisWarnings;
+    this.documentationLinkGenerator = documentationLinkGenerator;
   }
 
   public void publish() {
@@ -79,7 +82,7 @@ public final class ScmPublisher {
     if (!filesToBlame.isEmpty()) {
       String key = provider.key();
       LOG.info("SCM Publisher SCM provider for this project is: " + key);
-      DefaultBlameOutput output = new DefaultBlameOutput(writer, analysisWarnings, filesToBlame);
+      DefaultBlameOutput output = new DefaultBlameOutput(writer, analysisWarnings, filesToBlame, documentationLinkGenerator);
       try {
         provider.blameCommand().blame(new DefaultBlameInput(fs, filesToBlame), output);
       } catch (Exception e) {
index aec033458e7375f7d89c93ae28071ecc8ffb9c15..817f2370d044f7347a11477bce84d716e01d862e 100644 (file)
@@ -19,6 +19,7 @@
  */
 package org.sonar.scm.git;
 
+import com.google.common.annotations.VisibleForTesting;
 import java.io.BufferedOutputStream;
 import java.io.File;
 import java.io.IOException;
@@ -69,6 +70,7 @@ import org.sonar.api.utils.MessageException;
 import org.sonar.api.utils.System2;
 import org.sonar.api.utils.log.Logger;
 import org.sonar.api.utils.log.Loggers;
+import org.sonar.core.documentation.DocumentationLinkGenerator;
 
 import static java.lang.String.format;
 import static java.util.function.Function.identity;
@@ -84,18 +86,22 @@ public class GitScmProvider extends ScmProvider {
   private static final Logger LOG = Loggers.get(GitScmProvider.class);
   private static final String COULD_NOT_FIND_REF = "Could not find ref '%s' in refs/heads, refs/remotes, refs/remotes/upstream or refs/remotes/origin";
   private static final String NO_MERGE_BASE_FOUND_MESSAGE = "No merge base found between HEAD and %s";
+  @VisibleForTesting
+  static final String SCM_INTEGRATION_DOCUMENTATION_SUFFIX = "/analyzing-source-code/scm-integration/";
   private final BlameCommand blameCommand;
   private final AnalysisWarnings analysisWarnings;
   private final GitIgnoreCommand gitIgnoreCommand;
   private final System2 system2;
-  private final String documentationLink;
+  private final DocumentationLinkGenerator documentationLinkGenerator;
+
+  public GitScmProvider(CompositeBlameCommand blameCommand, AnalysisWarnings analysisWarnings, GitIgnoreCommand gitIgnoreCommand, System2 system2,
+    DocumentationLinkGenerator documentationLinkGenerator) {
 
-  public GitScmProvider(CompositeBlameCommand blameCommand, AnalysisWarnings analysisWarnings, GitIgnoreCommand gitIgnoreCommand, System2 system2) {
     this.blameCommand = blameCommand;
     this.analysisWarnings = analysisWarnings;
     this.gitIgnoreCommand = gitIgnoreCommand;
     this.system2 = system2;
-    this.documentationLink = "/documentation/analyzing-source-code/scm-integration/";
+    this.documentationLinkGenerator = documentationLinkGenerator;
   }
 
   @Override
@@ -257,10 +263,11 @@ public class GitScmProvider extends ScmProvider {
   }
 
   private void addWarningTargetNotFound(String targetBranchName) {
+    String url = documentationLinkGenerator.getDocumentationLink(SCM_INTEGRATION_DOCUMENTATION_SUFFIX);
     analysisWarnings.addUnique(format(COULD_NOT_FIND_REF
       + ". You may see unexpected issues and changes. "
       + "Please make sure to fetch this ref before pull request analysis and refer to"
-      + " <a href=\"%s\" target=\"_blank\">the documentation</a>.", targetBranchName, documentationLink));
+      + " <a href=\"%s\" rel=\"noopener noreferrer\" target=\"_blank\">the documentation</a>.", targetBranchName, url));
   }
 
   private void collectChangedLines(Repository repo, RevCommit mergeBaseCommit, Map<Path, Set<Integer>> changedLines, Path changedFilePath, ChangedFile changedFile) {
index 5e1972b4b80a3fd1e3cd882fc999f7e5e15883f3..9fc3d7104f5d4b5ed25fecd608cc5b417b97374b 100644 (file)
@@ -25,39 +25,58 @@ import org.sonar.api.batch.fs.InputFile;
 import org.sonar.api.batch.fs.internal.TestInputFileBuilder;
 import org.sonar.api.batch.scm.BlameLine;
 import org.sonar.api.utils.System2;
+import org.sonar.core.documentation.DocumentationLinkGenerator;
 import org.sonar.scanner.notifications.DefaultAnalysisWarnings;
 
 import static java.util.Collections.singletonList;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.sonar.scanner.scm.DefaultBlameOutput.SCM_INTEGRATION_DOCUMENTATION_SUFFIX;
 
 public class DefaultBlameOutputTest {
 
+  private static final String DOC_LINK = "https://link.to.documentation/new/page";
   private System2 system2 = mock(System2.class);
   private DefaultAnalysisWarnings analysisWarnings = new DefaultAnalysisWarnings(system2);
+  private DocumentationLinkGenerator documentationLinkGenerator = mock(DocumentationLinkGenerator.class);
 
   @Test
   public void shouldNotFailIfNotSameNumberOfLines() {
     InputFile file = new TestInputFileBuilder("foo", "src/main/java/Foo.java").setLines(10).build();
 
-    new DefaultBlameOutput(null, analysisWarnings, singletonList(file)).blameResult(file, singletonList(new BlameLine().revision("1").author("guy")));
+    new DefaultBlameOutput(null, analysisWarnings, singletonList(file), mock(DocumentationLinkGenerator.class)).blameResult(file,
+      singletonList(new BlameLine().revision("1").author("guy")));
   }
 
   @Test
   public void addWarningIfFilesMissing() {
+    when(documentationLinkGenerator.getDocumentationLink(SCM_INTEGRATION_DOCUMENTATION_SUFFIX)).thenReturn(DOC_LINK);
     InputFile file = new TestInputFileBuilder("foo", "src/main/java/Foo.java").setLines(10).build();
-    new DefaultBlameOutput(null, analysisWarnings, singletonList(file)).finish(true);
+    new DefaultBlameOutput(null, analysisWarnings, singletonList(file), documentationLinkGenerator).finish(true);
     assertThat(analysisWarnings.warnings()).extracting(DefaultAnalysisWarnings.Message::getText)
       .containsOnly("Missing blame information for 1 file. This may lead to some features not working correctly. " +
-        "Please check the analysis logs and refer to <a href=\"/documentation/analyzing-source-code/scm-integration/\" target=\"_blank\">the documentation</a>.");
+        "Please check the analysis logs and refer to <a href=\"" + DOC_LINK + "\" rel=\"noopener noreferrer\" target=\"_blank\">the documentation</a>.");
+  }
+
+  @Test
+  public void addWarningWithContextIfFiles() {
+    when(documentationLinkGenerator.getDocumentationLink(SCM_INTEGRATION_DOCUMENTATION_SUFFIX)).thenReturn(DOC_LINK);
+    InputFile file = new TestInputFileBuilder("foo", "src/main/java/Foo.java").setLines(10).build();
+    when(documentationLinkGenerator.getDocumentationLink("suffix")).thenReturn("blababl");
+    new DefaultBlameOutput(null, analysisWarnings, singletonList(file), documentationLinkGenerator).finish(true);
+    assertThat(analysisWarnings.warnings()).extracting(DefaultAnalysisWarnings.Message::getText)
+      .containsOnly("Missing blame information for 1 file. This may lead to some features not working correctly. " +
+        "Please check the analysis logs and refer to <a href=\"" + DOC_LINK + "\" rel=\"noopener noreferrer\" target=\"_blank\">the documentation</a>.");
   }
 
   @Test
   public void shouldFailIfNotExpectedFile() {
     InputFile file = new TestInputFileBuilder("foo", "src/main/java/Foo.java").build();
     var blameOutput = new DefaultBlameOutput(null, analysisWarnings,
-      singletonList(new TestInputFileBuilder("foo", "src/main/java/Foo2.java").build()));
+      singletonList(new TestInputFileBuilder("foo", "src/main/java/Foo2.java").build()),
+      mock(DocumentationLinkGenerator.class));
     var lines = singletonList(new BlameLine().revision("1").author("guy"));
 
     assertThatThrownBy(() -> blameOutput.blameResult(file, lines))
@@ -68,7 +87,7 @@ public class DefaultBlameOutputTest {
   @Test
   public void shouldFailIfNullDate() {
     InputFile file = new TestInputFileBuilder("foo", "src/main/java/Foo.java").setLines(1).build();
-    var blameOutput = new DefaultBlameOutput(null, analysisWarnings, singletonList(file));
+    var blameOutput = new DefaultBlameOutput(null, analysisWarnings, singletonList(file), mock(DocumentationLinkGenerator.class));
     var lines = singletonList(new BlameLine().revision("1").author("guy"));
 
     assertThatThrownBy(() -> blameOutput.blameResult(file, lines))
@@ -79,7 +98,7 @@ public class DefaultBlameOutputTest {
   @Test
   public void shouldFailIfNullRevision() {
     InputFile file = new TestInputFileBuilder("foo", "src/main/java/Foo.java").setLines(1).build();
-    var blameOUtput = new DefaultBlameOutput(null, analysisWarnings, singletonList(file));
+    var blameOUtput = new DefaultBlameOutput(null, analysisWarnings, singletonList(file), mock(DocumentationLinkGenerator.class));
     var lines = singletonList(new BlameLine().date(new Date()).author("guy"));
 
     assertThatThrownBy(() -> blameOUtput.blameResult(file, lines))
index e8e2d7bcebebcf685303ee003359e42873ed0fda..186677e7df448c9c7739279f69f0f9912cdb2a82 100644 (file)
@@ -60,6 +60,7 @@ import org.sonar.api.utils.MessageException;
 import org.sonar.api.utils.System2;
 import org.sonar.api.utils.log.LogAndArguments;
 import org.sonar.api.utils.log.LogTester;
+import org.sonar.core.documentation.DocumentationLinkGenerator;
 
 import static java.lang.String.format;
 import static java.util.Collections.emptySet;
@@ -74,6 +75,7 @@ import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoInteractions;
 import static org.mockito.Mockito.when;
 import static org.sonar.api.utils.log.LoggerLevel.WARN;
+import static org.sonar.scm.git.GitScmProvider.SCM_INTEGRATION_DOCUMENTATION_SUFFIX;
 import static org.sonar.scm.git.GitUtils.createFile;
 import static org.sonar.scm.git.GitUtils.createRepository;
 import static org.sonar.scm.git.Utils.javaUnzip;
@@ -109,6 +111,7 @@ public class GitScmProviderTest {
     + "The door of all subtleties!";
 
   private static final String BRANCH_NAME = "branch";
+  private static final String TEST_DOC_LINK = "documentation link";
 
   @Rule
   public TemporaryFolder temp = new TemporaryFolder();
@@ -123,11 +126,13 @@ public class GitScmProviderTest {
   private Path worktree;
   private Git git;
   private final AnalysisWarnings analysisWarnings = mock(AnalysisWarnings.class);
+  private final DocumentationLinkGenerator documentationLinkGenerator = mock(DocumentationLinkGenerator.class);
 
   @Before
   public void before() throws IOException, GitAPIException {
     worktree = temp.newFolder().toPath();
     git = createRepository(worktree);
+    when(documentationLinkGenerator.getDocumentationLink(SCM_INTEGRATION_DOCUMENTATION_SUFFIX)).thenReturn(TEST_DOC_LINK);
 
     createAndCommitFile("file-in-first-commit.xoo");
   }
@@ -142,7 +147,7 @@ public class GitScmProviderTest {
     JGitBlameCommand jblameCommand = new JGitBlameCommand();
     GitBlameCommand nativeBlameCommand = new GitBlameCommand(System2.INSTANCE, new ProcessWrapperFactory());
     CompositeBlameCommand compositeBlameCommand = new CompositeBlameCommand(analysisWarnings, new PathResolver(), jblameCommand, nativeBlameCommand);
-    GitScmProvider gitScmProvider = new GitScmProvider(compositeBlameCommand, analysisWarnings, gitIgnoreCommand, system2);
+    GitScmProvider gitScmProvider = new GitScmProvider(compositeBlameCommand, analysisWarnings, gitIgnoreCommand, system2, documentationLinkGenerator);
 
     assertThat(gitScmProvider.blameCommand()).isEqualTo(compositeBlameCommand);
   }
@@ -419,7 +424,8 @@ public class GitScmProviderTest {
   }
 
   @Test
-  public void branchChangedLinesWithFileMovementDetection_detects_modified_lines_on_renamed_pr_files_compared_to_original_files_on_target_branch() throws GitAPIException, IOException {
+  public void branchChangedLinesWithFileMovementDetection_detects_modified_lines_on_renamed_pr_files_compared_to_original_files_on_target_branch()
+    throws GitAPIException, IOException {
     String fileM1 = "file-m1.xoo";
     String newFileM1 = "new-file-m1.xoo";
     String fileM2 = "file-m2.xoo";
@@ -616,7 +622,7 @@ public class GitScmProviderTest {
 
   @Test
   public void branchChangedFiles_should_return_null_on_io_errors_of_repo_builder() {
-    GitScmProvider provider = new GitScmProvider(mockCommand(), analysisWarnings, gitIgnoreCommand, system2) {
+    GitScmProvider provider = new GitScmProvider(mockCommand(), analysisWarnings, gitIgnoreCommand, system2, documentationLinkGenerator) {
       @Override
       Repository buildRepo(Path basedir) throws IOException {
         throw new IOException();
@@ -633,7 +639,7 @@ public class GitScmProviderTest {
     when(repository.getRefDatabase()).thenReturn(refDatabase);
     when(refDatabase.findRef(BRANCH_NAME)).thenReturn(null);
 
-    GitScmProvider provider = new GitScmProvider(mockCommand(), analysisWarnings, gitIgnoreCommand, system2) {
+    GitScmProvider provider = new GitScmProvider(mockCommand(), analysisWarnings, gitIgnoreCommand, system2, documentationLinkGenerator) {
       @Override
       Repository buildRepo(Path basedir) {
         return repository;
@@ -648,7 +654,7 @@ public class GitScmProviderTest {
 
     String warning = refNotFound
       + ". You may see unexpected issues and changes. Please make sure to fetch this ref before pull request analysis"
-      + " and refer to <a href=\"/documentation/analyzing-source-code/scm-integration/\" target=\"_blank\">the documentation</a>.";
+      + " and refer to <a href=\"" + TEST_DOC_LINK + "\" rel=\"noopener noreferrer\" target=\"_blank\">the documentation</a>.";
     verify(analysisWarnings).addUnique(warning);
   }
 
@@ -663,7 +669,7 @@ public class GitScmProviderTest {
     Git git = mock(Git.class);
     when(git.diff()).thenReturn(diffCommand);
 
-    GitScmProvider provider = new GitScmProvider(mockCommand(), analysisWarnings, gitIgnoreCommand, system2) {
+    GitScmProvider provider = new GitScmProvider(mockCommand(), analysisWarnings, gitIgnoreCommand, system2, documentationLinkGenerator) {
       @Override
       Git newGit(Repository repo) {
         return git;
@@ -700,7 +706,7 @@ public class GitScmProviderTest {
     commit(f3);
 
     AtomicInteger callCount = new AtomicInteger(0);
-    GitScmProvider provider = new GitScmProvider(mockCommand(), analysisWarnings, gitIgnoreCommand, system2) {
+    GitScmProvider provider = new GitScmProvider(mockCommand(), analysisWarnings, gitIgnoreCommand, system2, documentationLinkGenerator) {
       @Override
       AbstractTreeIterator prepareTreeParser(Repository repo, RevCommit commit) throws IOException {
         if (callCount.getAndIncrement() == 2) {
@@ -721,7 +727,7 @@ public class GitScmProviderTest {
 
   @Test
   public void branchChangedLines_returns_null_on_io_errors_of_repo_builder() {
-    GitScmProvider provider = new GitScmProvider(mockCommand(), analysisWarnings, gitIgnoreCommand, system2) {
+    GitScmProvider provider = new GitScmProvider(mockCommand(), analysisWarnings, gitIgnoreCommand, system2, documentationLinkGenerator) {
       @Override
       Repository buildRepo(Path basedir) throws IOException {
         throw new IOException();
@@ -736,7 +742,7 @@ public class GitScmProviderTest {
   }
 
   private GitScmProvider newGitScmProvider() {
-    return new GitScmProvider(mock(CompositeBlameCommand.class), analysisWarnings, gitIgnoreCommand, system2);
+    return new GitScmProvider(mock(CompositeBlameCommand.class), analysisWarnings, gitIgnoreCommand, system2, documentationLinkGenerator);
   }
 
   @Test
@@ -814,12 +820,6 @@ public class GitScmProviderTest {
     createAndCommitFile(relativePath, randomizedContent(relativePath, 3), git, this.worktree);
   }
 
-  private void createAndCommitFile(String relativePath, Instant commitDate) throws IOException, GitAPIException {
-    createFile(relativePath, randomizedContent(relativePath, 3), this.worktree);
-    commit(relativePath, commitDate);
-  }
-
-
   private void createAndCommitFile(String fileName, Git git, Path worktree) throws IOException, GitAPIException {
     createAndCommitFile(fileName, randomizedContent(fileName, 3), git, worktree);
   }
@@ -896,6 +896,6 @@ public class GitScmProviderTest {
   }
 
   private GitScmProvider newScmProvider() {
-    return new GitScmProvider(mockCommand(), analysisWarnings, gitIgnoreCommand, system2);
+    return new GitScmProvider(mockCommand(), analysisWarnings, gitIgnoreCommand, system2, documentationLinkGenerator);
   }
 }