Browse Source

SONAR-11245 Record a warning when blame information is missing on some files during analysis

tags/8.1.0.31237
Duarte Meneses 4 years ago
parent
commit
80f0a0e9ab

+ 1
- 1
sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/CharsetDetector.java View File



public class CharsetDetector { public class CharsetDetector {
private static final int BYTES_TO_DECODE = 4192; private static final int BYTES_TO_DECODE = 4192;
private Path filePath;
private final Path filePath;
private BufferedInputStream stream; private BufferedInputStream stream;
private Charset detectedCharset; private Charset detectedCharset;
private Charset userEncoding; private Charset userEncoding;

+ 7
- 1
sonar-scanner-engine/src/main/java/org/sonar/scanner/scm/DefaultBlameOutput.java View File

import org.sonar.api.batch.fs.internal.DefaultInputFile; import org.sonar.api.batch.fs.internal.DefaultInputFile;
import org.sonar.api.batch.scm.BlameCommand.BlameOutput; import org.sonar.api.batch.scm.BlameCommand.BlameOutput;
import org.sonar.api.batch.scm.BlameLine; 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.Logger;
import org.sonar.api.utils.log.Loggers; import org.sonar.api.utils.log.Loggers;
import org.sonar.scanner.protocol.output.ScannerReport; import org.sonar.scanner.protocol.output.ScannerReport;
private static final Logger LOG = Loggers.get(DefaultBlameOutput.class); private static final Logger LOG = Loggers.get(DefaultBlameOutput.class);


private final ScannerReportWriter writer; private final ScannerReportWriter writer;
private AnalysisWarnings analysisWarnings;
private final Set<InputFile> allFilesToBlame = new LinkedHashSet<>(); private final Set<InputFile> allFilesToBlame = new LinkedHashSet<>();
private ProgressReport progressReport; private ProgressReport progressReport;
private int count; private int count;
private int total; private int total;


DefaultBlameOutput(ScannerReportWriter writer, List<InputFile> filesToBlame) {
DefaultBlameOutput(ScannerReportWriter writer, AnalysisWarnings analysisWarnings, List<InputFile> filesToBlame) {
this.writer = writer; this.writer = writer;
this.analysisWarnings = analysisWarnings;
this.allFilesToBlame.addAll(filesToBlame); this.allFilesToBlame.addAll(filesToBlame);
count = 0; count = 0;
total = filesToBlame.size(); total = filesToBlame.size();
LOG.warn(" * " + f); LOG.warn(" * " + f);
} }
LOG.warn("This may lead to missing/broken features in SonarQube"); LOG.warn("This may lead to missing/broken features in SonarQube");
analysisWarnings.addUnique(String.format("Missing blame information for %d %s. This may lead to some features not working correctly. Please check the analysis logs.",
allFilesToBlame.size(),
allFilesToBlame.size() > 1 ? "files" : "file"));
} }
} }



+ 5
- 2
sonar-scanner-engine/src/main/java/org/sonar/scanner/scm/ScmPublisher.java View File

import org.sonar.api.batch.fs.InputFile.Status; import org.sonar.api.batch.fs.InputFile.Status;
import org.sonar.api.batch.fs.internal.DefaultInputFile; import org.sonar.api.batch.fs.internal.DefaultInputFile;
import org.sonar.api.batch.scm.ScmProvider; 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.Logger;
import org.sonar.api.utils.log.Loggers; import org.sonar.api.utils.log.Loggers;
import org.sonar.scanner.protocol.output.ScannerReport; import org.sonar.scanner.protocol.output.ScannerReport;
private final InputComponentStore componentStore; private final InputComponentStore componentStore;
private final FileSystem fs; private final FileSystem fs;
private final ScannerReportWriter writer; private final ScannerReportWriter writer;
private AnalysisWarnings analysisWarnings;
private final BranchConfiguration branchConfiguration; private final BranchConfiguration branchConfiguration;


public ScmPublisher(ScmConfiguration configuration, ProjectRepositoriesSupplier projectRepositoriesSupplier, public ScmPublisher(ScmConfiguration configuration, ProjectRepositoriesSupplier projectRepositoriesSupplier,
InputComponentStore componentStore, FileSystem fs, ReportPublisher reportPublisher, BranchConfiguration branchConfiguration) {
InputComponentStore componentStore, FileSystem fs, ReportPublisher reportPublisher, BranchConfiguration branchConfiguration, AnalysisWarnings analysisWarnings) {
this.configuration = configuration; this.configuration = configuration;
this.projectRepositoriesSupplier = projectRepositoriesSupplier; this.projectRepositoriesSupplier = projectRepositoriesSupplier;
this.componentStore = componentStore; this.componentStore = componentStore;
this.fs = fs; this.fs = fs;
this.branchConfiguration = branchConfiguration; this.branchConfiguration = branchConfiguration;
this.writer = reportPublisher.getWriter(); this.writer = reportPublisher.getWriter();
this.analysisWarnings = analysisWarnings;
} }


public void publish() { public void publish() {
if (!filesToBlame.isEmpty()) { if (!filesToBlame.isEmpty()) {
String key = provider.key(); String key = provider.key();
LOG.info("SCM Publisher SCM provider for this project is: " + key); LOG.info("SCM Publisher SCM provider for this project is: " + key);
DefaultBlameOutput output = new DefaultBlameOutput(writer, filesToBlame);
DefaultBlameOutput output = new DefaultBlameOutput(writer, analysisWarnings, filesToBlame);
try { try {
provider.blameCommand().blame(new DefaultBlameInput(fs, filesToBlame), output); provider.blameCommand().blame(new DefaultBlameInput(fs, filesToBlame), output);
} catch (Exception e) { } catch (Exception e) {

+ 27
- 7
sonar-scanner-engine/src/test/java/org/sonar/scanner/scm/DefaultBlameOutputTest.java View File

package org.sonar.scanner.scm; package org.sonar.scanner.scm;


import java.util.Arrays; import java.util.Arrays;
import java.util.Collections;
import java.util.Date; import java.util.Date;
import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.fs.InputFile;
import org.sonar.api.batch.fs.internal.TestInputFileBuilder; import org.sonar.api.batch.fs.internal.TestInputFileBuilder;
import org.sonar.api.batch.scm.BlameLine; import org.sonar.api.batch.scm.BlameLine;
import org.sonar.api.utils.System2;
import org.sonar.scanner.notifications.DefaultAnalysisWarnings;

import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;


public class DefaultBlameOutputTest { public class DefaultBlameOutputTest {


@Rule @Rule
public ExpectedException thrown = ExpectedException.none(); public ExpectedException thrown = ExpectedException.none();
private System2 system2 = mock(System2.class);
private DefaultAnalysisWarnings analysisWarnings = new DefaultAnalysisWarnings(system2);


@Test @Test
public void shouldNotFailIfNotSameNumberOfLines() { public void shouldNotFailIfNotSameNumberOfLines() {
InputFile file = new TestInputFileBuilder("foo", "src/main/java/Foo.java").setLines(10).build(); InputFile file = new TestInputFileBuilder("foo", "src/main/java/Foo.java").setLines(10).build();


new DefaultBlameOutput(null, Arrays.asList(file)).blameResult(file, Arrays.asList(new BlameLine().revision("1").author("guy")));
new DefaultBlameOutput(null, analysisWarnings, singletonList(file)).blameResult(file, singletonList(new BlameLine().revision("1").author("guy")));
}

@Test
public void addWarningIfFilesMissing() {
InputFile file = new TestInputFileBuilder("foo", "src/main/java/Foo.java").setLines(10).build();

new DefaultBlameOutput(null, analysisWarnings, singletonList(file)).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.");
} }


@Test @Test
thrown.expect(IllegalArgumentException.class); thrown.expect(IllegalArgumentException.class);
thrown.expectMessage("It was not expected to blame file " + file); thrown.expectMessage("It was not expected to blame file " + file);


new DefaultBlameOutput(null, Arrays.<InputFile>asList(new TestInputFileBuilder("foo", "src/main/java/Foo2.java").build()))
.blameResult(file, Arrays.asList(new BlameLine().revision("1").author("guy")));
new DefaultBlameOutput(null, analysisWarnings, singletonList(new TestInputFileBuilder("foo", "src/main/java/Foo2.java").build()))
.blameResult(file, singletonList(new BlameLine().revision("1").author("guy")));
} }


@Test @Test
thrown.expect(IllegalArgumentException.class); thrown.expect(IllegalArgumentException.class);
thrown.expectMessage("Blame date is null for file " + file + " at line 1"); thrown.expectMessage("Blame date is null for file " + file + " at line 1");


new DefaultBlameOutput(null, Arrays.<InputFile>asList(file))
.blameResult(file, Arrays.asList(new BlameLine().revision("1").author("guy")));
new DefaultBlameOutput(null, analysisWarnings, singletonList(file))
.blameResult(file, singletonList(new BlameLine().revision("1").author("guy")));
} }


@Test @Test
thrown.expect(IllegalArgumentException.class); thrown.expect(IllegalArgumentException.class);
thrown.expectMessage("Blame revision is blank for file " + file + " at line 1"); thrown.expectMessage("Blame revision is blank for file " + file + " at line 1");


new DefaultBlameOutput(null, Arrays.<InputFile>asList(file))
.blameResult(file, Arrays.asList(new BlameLine().date(new Date()).author("guy")));
new DefaultBlameOutput(null, analysisWarnings, singletonList(file))
.blameResult(file, singletonList(new BlameLine().date(new Date()).author("guy")));
} }


} }

Loading…
Cancel
Save