@@ -1,96 +0,0 @@ | |||
/* | |||
* SonarQube, open source software quality management tool. | |||
* Copyright (C) 2008-2014 SonarSource | |||
* mailto:contact AT sonarsource DOT com | |||
* | |||
* SonarQube 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. | |||
* | |||
* SonarQube 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.plugins.scm.git; | |||
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.batch.scm.BlameCommand; | |||
import org.sonar.api.utils.command.Command; | |||
import org.sonar.api.utils.command.CommandExecutor; | |||
import org.sonar.api.utils.command.StreamConsumer; | |||
import java.io.File; | |||
public class GitBlameCommand implements BlameCommand { | |||
private static final Logger LOG = LoggerFactory.getLogger(GitBlameCommand.class); | |||
private final CommandExecutor commandExecutor; | |||
public GitBlameCommand() { | |||
this(CommandExecutor.create()); | |||
} | |||
GitBlameCommand(CommandExecutor commandExecutor) { | |||
this.commandExecutor = commandExecutor; | |||
} | |||
@Override | |||
public void blame(FileSystem fs, Iterable<InputFile> files, BlameResult result) { | |||
for (InputFile inputFile : files) { | |||
String filename = inputFile.relativePath(); | |||
Command cl = createCommandLine(fs.baseDir(), filename); | |||
GitBlameConsumer consumer = new GitBlameConsumer(LOG); | |||
StringStreamConsumer stderr = new StringStreamConsumer(); | |||
int exitCode = execute(cl, consumer, stderr); | |||
if (exitCode != 0) { | |||
throw new IllegalStateException("The git blame command [" + cl.toString() + "] failed: " + stderr.getOutput()); | |||
} | |||
result.add(inputFile, consumer.getLines()); | |||
} | |||
} | |||
public int execute(Command cl, StreamConsumer consumer, StreamConsumer stderr) { | |||
LOG.info("Executing: " + cl); | |||
LOG.info("Working directory: " + cl.getDirectory().getAbsolutePath()); | |||
return commandExecutor.execute(cl, consumer, stderr, 10 * 1000); | |||
} | |||
private Command createCommandLine(File workingDirectory, String filename) { | |||
Command cl = Command.create("git"); | |||
cl.addArgument("blame"); | |||
if (workingDirectory != null) { | |||
cl.setDirectory(workingDirectory); | |||
} | |||
cl.addArgument("--porcelain"); | |||
cl.addArgument(filename); | |||
cl.addArgument("-w"); | |||
return cl; | |||
} | |||
private static class StringStreamConsumer implements StreamConsumer { | |||
private StringBuffer string = new StringBuffer(); | |||
private String ls = System.getProperty("line.separator"); | |||
@Override | |||
public void consumeLine(String line) { | |||
string.append(line + ls); | |||
} | |||
public String getOutput() { | |||
return string.toString(); | |||
} | |||
} | |||
} |
@@ -28,8 +28,7 @@ public final class GitPlugin extends SonarPlugin { | |||
public List getExtensions() { | |||
return ImmutableList.of( | |||
GitScmProvider.class, | |||
GitBlameCommand.class); | |||
GitScmProvider.class); | |||
} | |||
} |
@@ -19,18 +19,20 @@ | |||
*/ | |||
package org.sonar.plugins.scm.git; | |||
import org.sonar.api.batch.scm.BlameCommand; | |||
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.batch.scm.ScmProvider; | |||
import org.sonar.api.utils.command.Command; | |||
import org.sonar.api.utils.command.CommandExecutor; | |||
import org.sonar.api.utils.command.StreamConsumer; | |||
import java.io.File; | |||
public class GitScmProvider extends ScmProvider { | |||
public class GitScmProvider implements ScmProvider { | |||
private GitBlameCommand blameCommand; | |||
public GitScmProvider(GitBlameCommand blameCommand) { | |||
this.blameCommand = blameCommand; | |||
} | |||
private static final Logger LOG = LoggerFactory.getLogger(GitScmProvider.class); | |||
@Override | |||
public String key() { | |||
@@ -43,8 +45,53 @@ public class GitScmProvider extends ScmProvider { | |||
} | |||
@Override | |||
public BlameCommand blameCommand() { | |||
return this.blameCommand; | |||
public void blame(FileSystem fs, Iterable<InputFile> files, BlameResult result) { | |||
for (InputFile inputFile : files) { | |||
String filename = inputFile.relativePath(); | |||
Command cl = createCommandLine(fs.baseDir(), filename); | |||
SonarGitBlameConsumer consumer = new SonarGitBlameConsumer(LOG); | |||
StringStreamConsumer stderr = new StringStreamConsumer(); | |||
int exitCode = execute(cl, consumer, stderr); | |||
if (exitCode != 0) { | |||
throw new IllegalStateException("The git blame command [" + cl.toString() + "] failed: " + stderr.getOutput()); | |||
} | |||
result.add(inputFile, consumer.getLines()); | |||
} | |||
} | |||
public static int execute(Command cl, StreamConsumer consumer, StreamConsumer stderr) { | |||
LOG.info("Executing: " + cl); | |||
LOG.info("Working directory: " + cl.getDirectory().getAbsolutePath()); | |||
return CommandExecutor.create().execute(cl, consumer, stderr, 10 * 1000); | |||
} | |||
private static Command createCommandLine(File workingDirectory, String filename) { | |||
Command cl = Command.create("git"); | |||
cl.addArgument("blame"); | |||
if (workingDirectory != null) { | |||
cl.setDirectory(workingDirectory); | |||
} | |||
cl.addArgument("--porcelain"); | |||
cl.addArgument(filename); | |||
cl.addArgument("-w"); | |||
return cl; | |||
} | |||
private static class StringStreamConsumer implements StreamConsumer { | |||
private StringBuffer string = new StringBuffer(); | |||
private String ls = System.getProperty("line.separator"); | |||
@Override | |||
public void consumeLine(String line) { | |||
string.append(line + ls); | |||
} | |||
public String getOutput() { | |||
return string.toString(); | |||
} | |||
} | |||
} |
@@ -1,22 +1,23 @@ | |||
/* | |||
* SonarQube, open source software quality management tool. | |||
* Copyright (C) 2008-2014 SonarSource | |||
* mailto:contact AT sonarsource DOT com | |||
* Sonar SCM Activity Plugin | |||
* Copyright (C) 2010 SonarSource | |||
* dev@sonar.codehaus.org | |||
* | |||
* SonarQube is free software; you can redistribute it and/or | |||
* 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. | |||
* | |||
* SonarQube is distributed in the hope that it will be useful, | |||
* 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. | |||
* 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 02 | |||
*/ | |||
package org.sonar.plugins.scm.git; | |||
import com.google.common.annotations.VisibleForTesting; | |||
@@ -44,7 +45,7 @@ import java.util.Map; | |||
* | |||
* @since 1.5.1 | |||
*/ | |||
public class GitBlameConsumer implements StreamConsumer { | |||
public class SonarGitBlameConsumer implements StreamConsumer { | |||
private static final String GIT_COMMITTER_PREFIX = "committer"; | |||
private static final String GIT_COMMITTER_TIME = GIT_COMMITTER_PREFIX + "-time "; | |||
@@ -77,7 +78,7 @@ public class GitBlameConsumer implements StreamConsumer { | |||
return logger; | |||
} | |||
public GitBlameConsumer(Logger logger) { | |||
public SonarGitBlameConsumer(Logger logger) { | |||
this.logger = logger; | |||
} | |||
@@ -1,108 +0,0 @@ | |||
/* | |||
* SonarQube, open source software quality management tool. | |||
* Copyright (C) 2008-2014 SonarSource | |||
* mailto:contact AT sonarsource DOT com | |||
* | |||
* SonarQube 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. | |||
* | |||
* SonarQube 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.plugins.scm.git; | |||
import org.apache.commons.io.FileUtils; | |||
import org.junit.Before; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.junit.rules.ExpectedException; | |||
import org.junit.rules.TemporaryFolder; | |||
import org.mockito.invocation.InvocationOnMock; | |||
import org.mockito.stubbing.Answer; | |||
import org.sonar.api.batch.fs.InputFile; | |||
import org.sonar.api.batch.fs.internal.DefaultFileSystem; | |||
import org.sonar.api.batch.fs.internal.DefaultInputFile; | |||
import org.sonar.api.batch.scm.BlameCommand.BlameResult; | |||
import org.sonar.api.batch.scm.BlameLine; | |||
import org.sonar.api.utils.DateUtils; | |||
import org.sonar.api.utils.command.Command; | |||
import org.sonar.api.utils.command.CommandExecutor; | |||
import org.sonar.api.utils.command.StreamConsumer; | |||
import java.io.File; | |||
import java.io.IOException; | |||
import java.util.Arrays; | |||
import static org.mockito.Matchers.any; | |||
import static org.mockito.Matchers.anyLong; | |||
import static org.mockito.Mockito.mock; | |||
import static org.mockito.Mockito.verify; | |||
import static org.mockito.Mockito.when; | |||
public class GitBlameCommandTest { | |||
@Rule | |||
public TemporaryFolder temp = new TemporaryFolder(); | |||
@Rule | |||
public ExpectedException thrown = ExpectedException.none(); | |||
private DefaultFileSystem fs; | |||
private File baseDir; | |||
@Before | |||
public void prepare() throws IOException { | |||
baseDir = temp.newFolder(); | |||
fs = new DefaultFileSystem(); | |||
fs.setBaseDir(baseDir); | |||
} | |||
@Test | |||
public void testParsingOfOutput() throws IOException { | |||
File source = new File(baseDir, "src/foo.xoo"); | |||
FileUtils.write(source, "sample content"); | |||
DefaultInputFile inputFile = new DefaultInputFile("foo", "src/foo.xoo").setAbsolutePath(new File(baseDir, "src/foo.xoo").getAbsolutePath()); | |||
fs.add(inputFile); | |||
BlameResult result = mock(BlameResult.class); | |||
CommandExecutor commandExecutor = mock(CommandExecutor.class); | |||
when(commandExecutor.execute(any(Command.class), any(StreamConsumer.class), any(StreamConsumer.class), anyLong())).thenAnswer(new Answer<Integer>() { | |||
@Override | |||
public Integer answer(InvocationOnMock invocation) throws Throwable { | |||
StreamConsumer outConsumer = (StreamConsumer) invocation.getArguments()[1]; | |||
outConsumer.consumeLine("2c68c473da7fc293e12ca50f19380c5118be7ead 68 54 1"); | |||
outConsumer.consumeLine("author Simon Brandhof"); | |||
outConsumer.consumeLine("author-mail <simon.brandhof@gmail.com>"); | |||
outConsumer.consumeLine("author-time 1312534171"); | |||
outConsumer.consumeLine("author-tz +0200"); | |||
outConsumer.consumeLine("committer Simon Brandhof"); | |||
outConsumer.consumeLine("committer-mail <simon.brandhof@gmail.com>"); | |||
outConsumer.consumeLine("committer-time 1312534171"); | |||
outConsumer.consumeLine("committer-tz +0200"); | |||
outConsumer.consumeLine("summary Move to nexus.codehaus.org + configuration of maven release plugin is back"); | |||
outConsumer.consumeLine("previous 1bec1c3a77f6957175be13e4433110f7fc8e387e pom.xml"); | |||
outConsumer.consumeLine("filename pom.xml"); | |||
outConsumer.consumeLine("\t<id>codehaus-nexus-staging</id>"); | |||
outConsumer.consumeLine("2c68c473da7fc293e12ca50f19380c5118be7ead 72 60 1"); | |||
outConsumer.consumeLine("\t<url>${sonar.snapshotRepository.url}</url>"); | |||
return 0; | |||
} | |||
}); | |||
new GitBlameCommand(commandExecutor).blame(fs, Arrays.<InputFile>asList(inputFile), result); | |||
verify(result).add(inputFile, | |||
Arrays.asList(new BlameLine(DateUtils.parseDateTime("2011-08-05T10:49:31+0200"), "2c68c473da7fc293e12ca50f19380c5118be7ead", "simon.brandhof@gmail.com"), | |||
new BlameLine(DateUtils.parseDateTime("2011-08-05T10:49:31+0200"), "2c68c473da7fc293e12ca50f19380c5118be7ead", "simon.brandhof@gmail.com"))); | |||
} | |||
} |
@@ -27,6 +27,6 @@ public class GitPluginTest { | |||
@Test | |||
public void getExtensions() { | |||
assertThat(new GitPlugin().getExtensions()).hasSize(2); | |||
assertThat(new GitPlugin().getExtensions()).hasSize(1); | |||
} | |||
} |
@@ -1,53 +0,0 @@ | |||
/* | |||
* SonarQube, open source software quality management tool. | |||
* Copyright (C) 2008-2014 SonarSource | |||
* mailto:contact AT sonarsource DOT com | |||
* | |||
* SonarQube 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. | |||
* | |||
* SonarQube 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.plugins.scm.git; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.junit.rules.TemporaryFolder; | |||
import java.io.File; | |||
import java.io.IOException; | |||
import static org.fest.assertions.Assertions.assertThat; | |||
public class GitScmProviderTest { | |||
@Rule | |||
public TemporaryFolder temp = new TemporaryFolder(); | |||
@Test | |||
public void sanityCheck() { | |||
assertThat(new GitScmProvider(null).key()).isEqualTo("git"); | |||
GitBlameCommand blameCommand = new GitBlameCommand(); | |||
assertThat(new GitScmProvider(blameCommand).blameCommand()).isEqualTo(blameCommand); | |||
} | |||
@Test | |||
public void testAutodetection() throws IOException { | |||
File baseDirEmpty = temp.newFolder(); | |||
assertThat(new GitScmProvider(null).supports(baseDirEmpty)).isFalse(); | |||
File gitBaseDir = temp.newFolder(); | |||
new File(gitBaseDir, ".git").mkdir(); | |||
assertThat(new GitScmProvider(null).supports(gitBaseDir)).isTrue(); | |||
} | |||
} |
@@ -0,0 +1,119 @@ | |||
/* | |||
* SonarQube, open source software quality management tool. | |||
* Copyright (C) 2008-2014 SonarSource | |||
* mailto:contact AT sonarsource DOT com | |||
* | |||
* SonarQube 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. | |||
* | |||
* SonarQube 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.plugins.scm.git.medium; | |||
import com.google.common.collect.ImmutableMap; | |||
import org.apache.commons.io.FileUtils; | |||
import org.junit.After; | |||
import org.junit.Before; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.junit.rules.ExpectedException; | |||
import org.junit.rules.TemporaryFolder; | |||
import org.sonar.api.batch.fs.InputFile; | |||
import org.sonar.api.batch.sensor.duplication.DuplicationGroup; | |||
import org.sonar.batch.mediumtest.BatchMediumTester; | |||
import org.sonar.batch.mediumtest.BatchMediumTester.TaskResult; | |||
import org.sonar.plugins.scm.git.GitPlugin; | |||
import org.sonar.xoo.XooPlugin; | |||
import java.io.File; | |||
import java.io.IOException; | |||
import java.util.List; | |||
import static org.fest.assertions.Assertions.assertThat; | |||
public class GitMediumTest { | |||
@Rule | |||
public TemporaryFolder temp = new TemporaryFolder(); | |||
@Rule | |||
public ExpectedException thrown = ExpectedException.none(); | |||
public BatchMediumTester tester = BatchMediumTester.builder() | |||
.registerPlugin("xoo", new XooPlugin()) | |||
.registerPlugin("git", new GitPlugin()) | |||
.addDefaultQProfile("xoo", "Sonar Way") | |||
.bootstrapProperties(ImmutableMap.of("sonar.analysis.mode", "sensor")) | |||
.build(); | |||
private File baseDir; | |||
private ImmutableMap.Builder<String, String> builder; | |||
@Before | |||
public void prepare() throws IOException { | |||
tester.start(); | |||
baseDir = temp.newFolder(); | |||
builder = ImmutableMap.<String, String>builder() | |||
.put("sonar.task", "scan") | |||
.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"); | |||
} | |||
@After | |||
public void stop() { | |||
tester.stop(); | |||
} | |||
@Test | |||
public void testDuplications() throws IOException { | |||
File srcDir = new File(baseDir, "src"); | |||
srcDir.mkdir(); | |||
String duplicatedStuff = "Sample xoo\ncontent\nfoo\nbar\ntoto\ntiti\nfoo\nbar\ntoto\ntiti\nbar\ntoto\ntiti\nfoo\nbar\ntoto\ntiti"; | |||
File xooFile1 = new File(srcDir, "sample1.xoo"); | |||
FileUtils.write(xooFile1, duplicatedStuff); | |||
File xooFile2 = new File(srcDir, "sample2.xoo"); | |||
FileUtils.write(xooFile2, duplicatedStuff); | |||
TaskResult result = tester.newTask() | |||
.properties(builder | |||
.put("sonar.sources", "src") | |||
.put("sonar.cpd.xoo.minimumTokens", "10") | |||
.put("sonar.verbose", "true") | |||
.build()) | |||
.start(); | |||
assertThat(result.inputFiles()).hasSize(2); | |||
// 4 measures per file | |||
assertThat(result.measures()).hasSize(8); | |||
InputFile inputFile = result.inputFiles().get(0); | |||
// One clone group | |||
List<DuplicationGroup> duplicationGroups = result.duplicationsFor(inputFile); | |||
assertThat(duplicationGroups).hasSize(1); | |||
DuplicationGroup cloneGroup = duplicationGroups.get(0); | |||
assertThat(cloneGroup.duplicates()).hasSize(1); | |||
assertThat(cloneGroup.originBlock().startLine()).isEqualTo(1); | |||
assertThat(cloneGroup.originBlock().length()).isEqualTo(17); | |||
} | |||
} |
@@ -25,14 +25,13 @@ import org.sonar.xoo.lang.MeasureSensor; | |||
import org.sonar.xoo.lang.SymbolReferencesSensor; | |||
import org.sonar.xoo.lang.SyntaxHighlightingSensor; | |||
import org.sonar.xoo.lang.TestCaseSensor; | |||
import org.sonar.xoo.lang.XooScmProvider; | |||
import org.sonar.xoo.lang.XooTokenizerSensor; | |||
import org.sonar.xoo.rule.CreateIssueByInternalKeySensor; | |||
import org.sonar.xoo.rule.OneIssueOnDirPerFileSensor; | |||
import org.sonar.xoo.rule.OneIssuePerLineSensor; | |||
import org.sonar.xoo.rule.XooQualityProfile; | |||
import org.sonar.xoo.rule.XooRulesDefinition; | |||
import org.sonar.xoo.scm.XooBlameCommand; | |||
import org.sonar.xoo.scm.XooScmProvider; | |||
import java.util.Arrays; | |||
import java.util.List; | |||
@@ -54,7 +53,6 @@ public class XooPlugin extends SonarPlugin { | |||
// SCM | |||
XooScmProvider.class, | |||
XooBlameCommand.class, | |||
// sensors | |||
MeasureSensor.class, |
@@ -17,16 +17,18 @@ | |||
* 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.xoo.scm; | |||
package org.sonar.xoo.lang; | |||
import com.google.common.annotations.VisibleForTesting; | |||
import com.google.common.base.Charsets; | |||
import org.apache.commons.io.FileUtils; | |||
import org.apache.commons.lang.StringUtils; | |||
import org.sonar.api.batch.fs.FileSystem; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
import org.sonar.api.batch.fs.InputFile; | |||
import org.sonar.api.batch.scm.BlameCommand; | |||
import org.sonar.api.batch.scm.BlameLine; | |||
import org.sonar.api.batch.scm.ScmProvider; | |||
import org.sonar.api.config.Settings; | |||
import org.sonar.api.utils.DateUtils; | |||
import java.io.File; | |||
@@ -35,19 +37,37 @@ import java.util.ArrayList; | |||
import java.util.Date; | |||
import java.util.List; | |||
public class XooBlameCommand implements BlameCommand { | |||
public class XooScmProvider implements ScmProvider { | |||
private static final Logger LOG = LoggerFactory.getLogger(XooScmProvider.class); | |||
private static final String SCM_EXTENSION = ".scm"; | |||
private final Settings settings; | |||
public XooScmProvider(Settings settings) { | |||
this.settings = settings; | |||
} | |||
@Override | |||
public String key() { | |||
return "xoo"; | |||
} | |||
@Override | |||
public boolean supports(File baseDir) { | |||
return false; | |||
} | |||
@Override | |||
public void blame(FileSystem fs, Iterable<InputFile> files, BlameResult result) { | |||
public void blame(Iterable<InputFile> files, BlameResult handler) { | |||
for (InputFile inputFile : files) { | |||
processFile(inputFile, result); | |||
processFile(inputFile, handler); | |||
} | |||
} | |||
@VisibleForTesting | |||
protected void processFile(InputFile inputFile, BlameResult result) { | |||
protected void processFile(InputFile inputFile, BlameResult handler) { | |||
File ioFile = inputFile.file(); | |||
File scmDataFile = new java.io.File(ioFile.getParentFile(), ioFile.getName() + SCM_EXTENSION); | |||
if (!scmDataFile.exists()) { | |||
@@ -74,7 +94,7 @@ public class XooBlameCommand implements BlameCommand { | |||
blame.add(new BlameLine(date, revision, author)); | |||
} | |||
} | |||
result.add(inputFile, blame); | |||
handler.add(inputFile, blame); | |||
} catch (IOException e) { | |||
throw new IllegalStateException(e); | |||
} |
@@ -1,23 +0,0 @@ | |||
/* | |||
* SonarQube, open source software quality management tool. | |||
* Copyright (C) 2008-2014 SonarSource | |||
* mailto:contact AT sonarsource DOT com | |||
* | |||
* SonarQube 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. | |||
* | |||
* SonarQube 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. | |||
*/ | |||
@ParametersAreNonnullByDefault | |||
package org.sonar.xoo.lang; | |||
import javax.annotation.ParametersAreNonnullByDefault; |
@@ -32,7 +32,7 @@ public class OneIssuePerLineSensor implements Sensor { | |||
public static final String RULE_KEY = "OneIssuePerLine"; | |||
private static final String EFFORT_TO_FIX_PROPERTY = "sonar.oneIssuePerLine.effortToFix"; | |||
public static final String FORCE_SEVERITY_PROPERTY = "sonar.oneIssuePerLine.forceSeverity"; | |||
private static final String FORCE_SEVERITY_PROPERTY = "sonar.oneIssuePerLine.forceSeverity"; | |||
@Override | |||
public void describe(SensorDescriptor descriptor) { |
@@ -1,43 +0,0 @@ | |||
/* | |||
* SonarQube, open source software quality management tool. | |||
* Copyright (C) 2008-2014 SonarSource | |||
* mailto:contact AT sonarsource DOT com | |||
* | |||
* SonarQube 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. | |||
* | |||
* SonarQube 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.xoo.scm; | |||
import org.sonar.api.batch.scm.BlameCommand; | |||
import org.sonar.api.batch.scm.ScmProvider; | |||
public class XooScmProvider extends ScmProvider { | |||
private final XooBlameCommand blame; | |||
public XooScmProvider(XooBlameCommand blame) { | |||
this.blame = blame; | |||
} | |||
@Override | |||
public String key() { | |||
return "xoo"; | |||
} | |||
@Override | |||
public BlameCommand blameCommand() { | |||
return blame; | |||
} | |||
} |
@@ -1,23 +0,0 @@ | |||
/* | |||
* SonarQube, open source software quality management tool. | |||
* Copyright (C) 2008-2014 SonarSource | |||
* mailto:contact AT sonarsource DOT com | |||
* | |||
* SonarQube 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. | |||
* | |||
* SonarQube 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. | |||
*/ | |||
@ParametersAreNonnullByDefault | |||
package org.sonar.xoo.scm; | |||
import javax.annotation.ParametersAreNonnullByDefault; |
@@ -27,6 +27,6 @@ public class XooPluginTest { | |||
@Test | |||
public void provide_extensions() { | |||
assertThat(new XooPlugin().getExtensions()).hasSize(14); | |||
assertThat(new XooPlugin().getExtensions()).hasSize(13); | |||
} | |||
} |
@@ -1,104 +0,0 @@ | |||
/* | |||
* SonarQube, open source software quality management tool. | |||
* Copyright (C) 2008-2014 SonarSource | |||
* mailto:contact AT sonarsource DOT com | |||
* | |||
* SonarQube 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. | |||
* | |||
* SonarQube 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.xoo.rule; | |||
import org.junit.Test; | |||
import org.mockito.ArgumentCaptor; | |||
import org.mockito.invocation.InvocationOnMock; | |||
import org.mockito.stubbing.Answer; | |||
import org.sonar.api.batch.fs.internal.DefaultFileSystem; | |||
import org.sonar.api.batch.fs.internal.DefaultInputFile; | |||
import org.sonar.api.batch.sensor.SensorContext; | |||
import org.sonar.api.batch.sensor.SensorStorage; | |||
import org.sonar.api.batch.sensor.internal.DefaultSensorDescriptor; | |||
import org.sonar.api.batch.sensor.issue.Issue; | |||
import org.sonar.api.batch.sensor.issue.Issue.Severity; | |||
import org.sonar.api.batch.sensor.issue.internal.DefaultIssue; | |||
import org.sonar.api.config.Settings; | |||
import org.sonar.xoo.Xoo; | |||
import static org.fest.assertions.Assertions.assertThat; | |||
import static org.mockito.Mockito.mock; | |||
import static org.mockito.Mockito.times; | |||
import static org.mockito.Mockito.verify; | |||
import static org.mockito.Mockito.when; | |||
public class OneIssuePerLineSensorTest { | |||
private OneIssuePerLineSensor sensor = new OneIssuePerLineSensor(); | |||
@Test | |||
public void testDescriptor() { | |||
DefaultSensorDescriptor descriptor = new DefaultSensorDescriptor(); | |||
sensor.describe(descriptor); | |||
assertThat(descriptor.ruleRepositories()).containsOnly(XooRulesDefinition.XOO_REPOSITORY); | |||
} | |||
@Test | |||
public void testRule() { | |||
DefaultFileSystem fs = new DefaultFileSystem(); | |||
DefaultInputFile inputFile = new DefaultInputFile("foo", "src/Foo.xoo").setLanguage(Xoo.KEY).setLines(10); | |||
fs.add(inputFile); | |||
SensorContext context = mock(SensorContext.class); | |||
final SensorStorage sensorStorage = mock(SensorStorage.class); | |||
when(context.settings()).thenReturn(new Settings()); | |||
when(context.fileSystem()).thenReturn(fs); | |||
when(context.newIssue()).thenAnswer(new Answer<Issue>() { | |||
@Override | |||
public Issue answer(InvocationOnMock invocation) throws Throwable { | |||
return new DefaultIssue(sensorStorage); | |||
} | |||
}); | |||
sensor.execute(context); | |||
ArgumentCaptor<DefaultIssue> argCaptor = ArgumentCaptor.forClass(DefaultIssue.class); | |||
verify(sensorStorage, times(10)).store(argCaptor.capture()); | |||
assertThat(argCaptor.getAllValues()).hasSize(10); // One issue per line | |||
assertThat(argCaptor.getValue().overridenSeverity()).isNull(); | |||
} | |||
@Test | |||
public void testForceSeverity() { | |||
DefaultFileSystem fs = new DefaultFileSystem(); | |||
DefaultInputFile inputFile = new DefaultInputFile("foo", "src/Foo.xoo").setLanguage(Xoo.KEY).setLines(10); | |||
fs.add(inputFile); | |||
SensorContext context = mock(SensorContext.class); | |||
final SensorStorage sensorStorage = mock(SensorStorage.class); | |||
Settings settings = new Settings(); | |||
settings.setProperty(OneIssuePerLineSensor.FORCE_SEVERITY_PROPERTY, "MINOR"); | |||
when(context.settings()).thenReturn(settings); | |||
when(context.fileSystem()).thenReturn(fs); | |||
when(context.newIssue()).thenAnswer(new Answer<Issue>() { | |||
@Override | |||
public Issue answer(InvocationOnMock invocation) throws Throwable { | |||
return new DefaultIssue(sensorStorage); | |||
} | |||
}); | |||
sensor.execute(context); | |||
ArgumentCaptor<DefaultIssue> argCaptor = ArgumentCaptor.forClass(DefaultIssue.class); | |||
verify(sensorStorage, times(10)).store(argCaptor.capture()); | |||
assertThat(argCaptor.getAllValues()).hasSize(10); // One issue per line | |||
assertThat(argCaptor.getValue().overridenSeverity()).isEqualTo(Severity.MINOR); | |||
} | |||
} |
@@ -1,75 +0,0 @@ | |||
/* | |||
* SonarQube, open source software quality management tool. | |||
* Copyright (C) 2008-2014 SonarSource | |||
* mailto:contact AT sonarsource DOT com | |||
* | |||
* SonarQube 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. | |||
* | |||
* SonarQube 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.xoo.scm; | |||
import org.apache.commons.io.FileUtils; | |||
import org.junit.Before; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.junit.rules.ExpectedException; | |||
import org.junit.rules.TemporaryFolder; | |||
import org.sonar.api.batch.fs.InputFile; | |||
import org.sonar.api.batch.fs.internal.DefaultFileSystem; | |||
import org.sonar.api.batch.fs.internal.DefaultInputFile; | |||
import org.sonar.api.batch.scm.BlameCommand.BlameResult; | |||
import org.sonar.api.batch.scm.BlameLine; | |||
import org.sonar.api.utils.DateUtils; | |||
import org.sonar.xoo.Xoo; | |||
import java.io.File; | |||
import java.io.IOException; | |||
import java.util.Arrays; | |||
import static org.mockito.Mockito.mock; | |||
import static org.mockito.Mockito.verify; | |||
public class XooBlameCommandTest { | |||
@Rule | |||
public TemporaryFolder temp = new TemporaryFolder(); | |||
@Rule | |||
public ExpectedException thrown = ExpectedException.none(); | |||
private DefaultFileSystem fs; | |||
private File baseDir; | |||
@Before | |||
public void prepare() throws IOException { | |||
baseDir = temp.newFolder(); | |||
fs = new DefaultFileSystem(); | |||
} | |||
@Test | |||
public void testBlame() throws IOException { | |||
File source = new File(baseDir, "src/foo.xoo"); | |||
FileUtils.write(source, "sample content"); | |||
File scm = new File(baseDir, "src/foo.xoo.scm"); | |||
FileUtils.write(scm, "123,julien,2014-12-12\n234,julien,2014-12-24"); | |||
DefaultInputFile inputFile = new DefaultInputFile("foo", "src/foo.xoo").setAbsolutePath(new File(baseDir, "src/foo.xoo").getAbsolutePath()).setLanguage(Xoo.KEY); | |||
fs.add(inputFile); | |||
BlameResult result = mock(BlameResult.class); | |||
new XooBlameCommand().blame(fs, Arrays.<InputFile>asList(inputFile), result); | |||
verify(result).add(inputFile, Arrays.asList(new BlameLine(DateUtils.parseDate("2014-12-12"), "123", "julien"), | |||
new BlameLine(DateUtils.parseDate("2014-12-24"), "234", "julien"))); | |||
} | |||
} |
@@ -34,7 +34,6 @@ | |||
<module>plugins/sonar-cpd-plugin</module> | |||
<module>plugins/sonar-l10n-en-plugin</module> | |||
<module>plugins/sonar-email-notifications-plugin</module> | |||
<module>plugins/sonar-git-plugin</module> | |||
<module>plugins/sonar-xoo-plugin</module> | |||
</modules> | |||
@@ -24,8 +24,8 @@ import org.slf4j.LoggerFactory; | |||
import org.sonar.api.batch.fs.FileSystem; | |||
import org.sonar.api.batch.fs.InputFile; | |||
import org.sonar.api.batch.fs.InputFile.Status; | |||
import org.sonar.api.batch.scm.BlameCommand.BlameResult; | |||
import org.sonar.api.batch.scm.BlameLine; | |||
import org.sonar.api.batch.scm.ScmProvider.BlameResult; | |||
import org.sonar.api.batch.sensor.Sensor; | |||
import org.sonar.api.batch.sensor.SensorContext; | |||
import org.sonar.api.batch.sensor.SensorDescriptor; | |||
@@ -92,7 +92,7 @@ public final class ScmActivitySensor implements Sensor { | |||
filesToBlame.add(f); | |||
} | |||
} | |||
configuration.provider().blameCommand().blame(fs, filesToBlame, new BlameResult() { | |||
configuration.provider().blame(fs, filesToBlame, new BlameResult() { | |||
@Override | |||
public void add(InputFile file, List<BlameLine> lines) { |
@@ -1,47 +0,0 @@ | |||
/* | |||
* SonarQube, open source software quality management tool. | |||
* Copyright (C) 2008-2014 SonarSource | |||
* mailto:contact AT sonarsource DOT com | |||
* | |||
* SonarQube 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. | |||
* | |||
* SonarQube 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.api.batch.scm; | |||
import org.sonar.api.batch.fs.FileSystem; | |||
import org.sonar.api.batch.fs.InputFile; | |||
import java.util.List; | |||
/** | |||
* @since 5.0 | |||
*/ | |||
public interface BlameCommand { | |||
/** | |||
* Compute blame of the provided files. Computation can be done in parallel. | |||
* If there is an error that prevent to blame a file then an exception should be raised. | |||
*/ | |||
void blame(FileSystem fs, Iterable<InputFile> files, BlameResult result); | |||
/** | |||
* Callback for the provider to report results of blame per file. | |||
*/ | |||
public static interface BlameResult { | |||
void add(InputFile file, List<BlameLine> lines); | |||
} | |||
} |
@@ -19,11 +19,6 @@ | |||
*/ | |||
package org.sonar.api.batch.scm; | |||
import org.apache.commons.lang.builder.EqualsBuilder; | |||
import org.apache.commons.lang.builder.HashCodeBuilder; | |||
import org.apache.commons.lang.builder.ToStringBuilder; | |||
import org.apache.commons.lang.builder.ToStringStyle; | |||
import java.util.Date; | |||
/** | |||
@@ -104,41 +99,4 @@ public class BlameLine { | |||
this.date = null; | |||
} | |||
} | |||
// For testing purpose | |||
@Override | |||
public boolean equals(Object obj) { | |||
if (obj == null) { | |||
return false; | |||
} | |||
if (obj == this) { | |||
return true; | |||
} | |||
if (obj.getClass() != getClass()) { | |||
return false; | |||
} | |||
BlameLine rhs = (BlameLine) obj; | |||
return new EqualsBuilder() | |||
.append(date, rhs.date) | |||
.append(revision, rhs.revision) | |||
.append(author, rhs.author) | |||
.append(committer, rhs.committer) | |||
.isEquals(); | |||
} | |||
@Override | |||
public int hashCode() { | |||
return new HashCodeBuilder(27, 45). | |||
append(date) | |||
.append(revision) | |||
.append(author) | |||
.append(committer) | |||
.toHashCode(); | |||
} | |||
@Override | |||
public String toString() { | |||
return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE); | |||
} | |||
} |
@@ -21,31 +21,42 @@ package org.sonar.api.batch.scm; | |||
import org.sonar.api.BatchExtension; | |||
import org.sonar.api.batch.InstantiationStrategy; | |||
import org.sonar.api.batch.fs.FileSystem; | |||
import org.sonar.api.batch.fs.InputFile; | |||
import java.io.File; | |||
import java.util.List; | |||
/** | |||
* @since 5.0 | |||
*/ | |||
@InstantiationStrategy(InstantiationStrategy.PER_BATCH) | |||
public abstract class ScmProvider implements BatchExtension { | |||
public interface ScmProvider extends BatchExtension { | |||
/** | |||
* Unique identifier of the provider. Can be used in SCM URL to define the provider to use. | |||
*/ | |||
public abstract String key(); | |||
String key(); | |||
/** | |||
* Does this provider able to manage files located in this directory. | |||
* Used by autodetection. Not considered if user has forced the provider key. | |||
* @return false by default | |||
* Used by autodetection. | |||
*/ | |||
public boolean supports(File baseDir) { | |||
return false; | |||
} | |||
boolean supports(File baseDir); | |||
/** | |||
* Compute blame of the provided files. Computation can be done in parallel. | |||
* If there is an error that prevent to blame a file then an exception should be raised. | |||
*/ | |||
void blame(FileSystem fs, Iterable<InputFile> files, BlameResult result); | |||
/** | |||
* Callback for the provider to save results of blame per file. | |||
*/ | |||
public static interface BlameResult { | |||
void add(InputFile file, List<BlameLine> lines); | |||
public BlameCommand blameCommand() { | |||
throw new UnsupportedOperationException("Blame command is not supported by " + key() + " provider"); | |||
} | |||
} |
@@ -52,12 +52,10 @@ public class DefaultIssue implements Issue { | |||
private final SensorStorage storage; | |||
public DefaultIssue() { | |||
this.key = UUID.randomUUID().toString(); | |||
this.storage = null; | |||
} | |||
public DefaultIssue(SensorStorage storage) { | |||
this.key = UUID.randomUUID().toString(); | |||
this.storage = storage; | |||
} | |||
@@ -157,6 +155,9 @@ public class DefaultIssue implements Issue { | |||
public void save() { | |||
Preconditions.checkNotNull(this.storage, "No persister on this object"); | |||
Preconditions.checkNotNull(this.ruleKey, "ruleKey is mandatory on issue"); | |||
if (this.key == null) { | |||
this.key = UUID.randomUUID().toString(); | |||
} | |||
Preconditions.checkState(!Strings.isNullOrEmpty(key), "Fail to generate issue key"); | |||
storage.store(this); |
@@ -43,10 +43,6 @@ public class DefaultTestCase implements TestCase { | |||
private TestCase.Type type = Type.UNIT; | |||
private String stackTrace; | |||
public DefaultTestCase() { | |||
this.storage = null; | |||
} | |||
public DefaultTestCase(SensorStorage storage) { | |||
this.storage = storage; | |||
} |