/* * SonarQube * Copyright (C) 2009-2017 SonarSource SA * mailto:info AT sonarsource DOT com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ package org.sonar.scanner.mediumtest.issuesmode; import com.google.common.collect.ImmutableMap; import java.io.File; import java.io.IOException; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.LinkedList; import java.util.List; import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.io.FileUtils; import org.apache.commons.io.filefilter.FileFilterUtils; import org.apache.commons.lang.StringUtils; import org.assertj.core.api.Condition; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.sonar.api.CoreProperties; import org.sonar.api.utils.log.LogTester; import org.sonar.api.utils.log.LoggerLevel; import org.sonar.batch.bootstrapper.IssueListener; import org.sonar.scanner.mediumtest.ScannerMediumTester; import org.sonar.scanner.issue.tracking.TrackedIssue; import org.sonar.scanner.mediumtest.TaskResult; import org.sonar.scanner.protocol.Constants.Severity; import org.sonar.scanner.protocol.input.ScannerInput.ServerIssue; import org.sonar.scanner.scan.report.ConsoleReport; import org.sonar.xoo.XooPlugin; import org.sonar.xoo.rule.XooRulesDefinition; import static org.assertj.core.api.Assertions.assertThat; public class IssueModeAndReportsMediumTest { @org.junit.Rule public TemporaryFolder temp = new TemporaryFolder(); @org.junit.Rule public LogTester logTester = new LogTester(); private static SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy"); private static Long date(String date) { try { return sdf.parse(date).getTime(); } catch (ParseException e) { throw new IllegalStateException(e); } } public ScannerMediumTester tester = ScannerMediumTester.builder() .bootstrapProperties(ImmutableMap.of(CoreProperties.ANALYSIS_MODE, CoreProperties.ANALYSIS_MODE_ISSUES)) .registerPlugin("xoo", new XooPlugin()) .addDefaultQProfile("xoo", "Sonar Way") .addRules(new XooRulesDefinition()) .addActiveRule("xoo", "OneIssuePerLine", null, "One issue per line", "MAJOR", null, "xoo") .addActiveRule("xoo", "OneIssueOnDirPerFile", null, "OneIssueOnDirPerFile", "MAJOR", null, "xoo") .addActiveRule("xoo", "OneIssuePerModule", null, "OneIssuePerModule", "MAJOR", null, "xoo") .setPreviousAnalysisDate(new Date()) // Existing issue that is still detected .mockServerIssue(ServerIssue.newBuilder().setKey("xyz") .setModuleKey("sample") .setPath("xources/hello/HelloJava.xoo") .setRuleRepository("xoo") .setRuleKey("OneIssuePerLine") .setLine(1) .setSeverity(Severity.MAJOR) .setCreationDate(date("14/03/2004")) .setChecksum(DigestUtils.md5Hex("packagehello;")) .setStatus("OPEN") .build()) // Existing issue that is no more detected (will be closed) .mockServerIssue(ServerIssue.newBuilder().setKey("resolved") .setModuleKey("sample") .setPath("xources/hello/HelloJava.xoo") .setRuleRepository("xoo") .setRuleKey("OneIssuePerLine") .setLine(1) .setSeverity(Severity.MAJOR) .setCreationDate(date("14/03/2004")) .setChecksum(DigestUtils.md5Hex("dontexist")) .setStatus("OPEN") .build()) // Existing issue on project that is still detected .mockServerIssue(ServerIssue.newBuilder().setKey("resolved-on-project") .setModuleKey("sample") .setRuleRepository("xoo") .setRuleKey("OneIssuePerModule") .setSeverity(Severity.CRITICAL) .setCreationDate(date("14/03/2004")) .setStatus("OPEN") .build()) .build(); @Before public void prepare() { tester.start(); } @After public void stop() { tester.stop(); } private File copyProject(String path) throws Exception { File projectDir = temp.newFolder(); File originalProjectDir = new File(IssueModeAndReportsMediumTest.class.getResource(path).toURI()); FileUtils.copyDirectory(originalProjectDir, projectDir, FileFilterUtils.notFileFilter(FileFilterUtils.nameFileFilter(".sonar"))); return projectDir; } @Test public void testIssueTracking() throws Exception { File projectDir = copyProject("/mediumtest/xoo/sample"); TaskResult result = tester .newScanTask(new File(projectDir, "sonar-project.properties")) .start(); int newIssues = 0; int openIssues = 0; int resolvedIssue = 0; for (TrackedIssue issue : result.trackedIssues()) { System.out .println(issue.getMessage() + " " + issue.key() + " " + issue.getRuleKey() + " " + issue.isNew() + " " + issue.resolution() + " " + issue.componentKey() + " " + issue.startLine()); if (issue.isNew()) { newIssues++; } else if (issue.resolution() != null) { resolvedIssue++; } else { openIssues++; } } System.out.println("new: " + newIssues + " open: " + openIssues + " resolved " + resolvedIssue); assertThat(newIssues).isEqualTo(16); assertThat(openIssues).isEqualTo(2); assertThat(resolvedIssue).isEqualTo(1); // progress report String logs = StringUtils.join(logTester.logs(LoggerLevel.INFO), "\n"); assertThat(logs).contains("Performing issue tracking"); assertThat(logs).contains("6/6 components tracked"); // assert that original fields of a matched issue are kept assertThat(result.trackedIssues()).haveExactly(1, new Condition<TrackedIssue>() { @Override public boolean matches(TrackedIssue value) { return value.isNew() == false && "resolved-on-project".equals(value.key()) && "OPEN".equals(value.status()) && new Date(date("14/03/2004")).equals(value.creationDate()); } }); } @Test public void testConsoleReport() throws Exception { File projectDir = copyProject("/mediumtest/xoo/sample"); tester .newScanTask(new File(projectDir, "sonar-project.properties")) .property("sonar.issuesReport.console.enable", "true") .start(); assertThat(getReportLog()).contains("+16 issues", "+16 major"); } @Test public void testPostJob() throws Exception { File projectDir = copyProject("/mediumtest/xoo/sample"); tester .newScanTask(new File(projectDir, "sonar-project.properties")) .property("sonar.xoo.enablePostJob", "true") .start(); assertThat(logTester.logs()).contains("Resolved issues: 1", "Open issues: 18"); } private String getReportLog() { for (String log : logTester.logs()) { if (log.contains(ConsoleReport.HEADER)) { return log; } } throw new IllegalStateException("No console report"); } @Test public void testHtmlReport() throws Exception { File projectDir = copyProject("/mediumtest/xoo/sample"); tester .newScanTask(new File(projectDir, "sonar-project.properties")) .property("sonar.issuesReport.html.enable", "true") .start(); assertThat(new File(projectDir, ".sonar/issues-report/issues-report.html")).exists(); assertThat(new File(projectDir, ".sonar/issues-report/issues-report-light.html")).exists(); } @Test public void testHtmlReportNoFile() throws Exception { File baseDir = temp.newFolder(); File srcDir = new File(baseDir, "src"); srcDir.mkdir(); tester.newTask() .properties(ImmutableMap.<String, String>builder() .put("sonar.task", "scan") .put("sonar.projectBaseDir", baseDir.getAbsolutePath()) .put("sonar.projectKey", "sample") .put("sonar.projectName", "Foo Project") .put("sonar.projectVersion", "1.0-SNAPSHOT") .put("sonar.projectDescription", "Description of Foo Project") .put("sonar.sources", "src") .put("sonar.issuesReport.html.enable", "true") .build()) .start(); assertThat(FileUtils.readFileToString(new File(baseDir, ".sonar/issues-report/issues-report.html"))).contains("No file analyzed"); assertThat(FileUtils.readFileToString(new File(baseDir, ".sonar/issues-report/issues-report-light.html"))).contains("No file analyzed"); } @Test public void testIssueCallback() throws Exception { File projectDir = copyProject("/mediumtest/xoo/sample"); IssueRecorder issueListener = new IssueRecorder(); TaskResult result = tester .newScanTask(new File(projectDir, "sonar-project.properties")) .setIssueListener(issueListener) .property("sonar.verbose", "true") .start(); assertThat(result.trackedIssues()).hasSize(19); assertThat(issueListener.issueList).hasSize(19); } private class IssueRecorder implements IssueListener { List<Issue> issueList = new LinkedList<>(); @Override public void handle(Issue issue) { issueList.add(issue); } } @Test public void noSyntaxHighlightingInIssuesMode() throws IOException { File baseDir = temp.newFolder(); File srcDir = new File(baseDir, "src"); srcDir.mkdir(); File xooFile = new File(srcDir, "sample.xoo"); File xoohighlightingFile = new File(srcDir, "sample.xoo.highlighting"); FileUtils.write(xooFile, "Sample xoo\ncontent plop"); FileUtils.write(xoohighlightingFile, "0:10:s\n11:18:k"); TaskResult result = tester.newTask() .properties(ImmutableMap.<String, String>builder() .put("sonar.projectBaseDir", baseDir.getAbsolutePath()) .put("sonar.projectKey", "com.foo.project") .put("sonar.projectName", "Foo Project") .put("sonar.projectVersion", "1.0-SNAPSHOT") .put("sonar.projectDescription", "Description of Foo Project") .put("sonar.sources", "src") .build()) .start(); assertThat(result.getReportReader().hasSyntaxHighlighting(1)).isFalse(); assertThat(result.getReportReader().hasSyntaxHighlighting(2)).isFalse(); assertThat(result.getReportReader().hasSyntaxHighlighting(3)).isFalse(); } }