aboutsummaryrefslogtreecommitdiffstats
path: root/plugins/sonar-git-plugin
diff options
context:
space:
mode:
authorJulien HENRY <julien.henry@sonarsource.com>2014-09-24 15:15:05 +0200
committerJulien HENRY <julien.henry@sonarsource.com>2014-09-25 16:25:24 +0200
commitb375ce53532d11c75c9c920b36f0c4692d123c87 (patch)
tree5d6204c5574c3b354fa99afa0c7fb2a44ce78493 /plugins/sonar-git-plugin
parent78fbdc2a8445e9131a10d2210b88d0e4d9927a14 (diff)
downloadsonarqube-b375ce53532d11c75c9c920b36f0c4692d123c87.tar.gz
sonarqube-b375ce53532d11c75c9c920b36f0c4692d123c87.zip
SONAR-5642 Initial work on Git provider
Diffstat (limited to 'plugins/sonar-git-plugin')
-rw-r--r--plugins/sonar-git-plugin/pom.xml77
-rw-r--r--plugins/sonar-git-plugin/src/main/java/org/sonar/plugins/scm/git/GitPlugin.java34
-rw-r--r--plugins/sonar-git-plugin/src/main/java/org/sonar/plugins/scm/git/GitScmProvider.java97
-rw-r--r--plugins/sonar-git-plugin/src/main/java/org/sonar/plugins/scm/git/SonarGitBlameConsumer.java190
-rw-r--r--plugins/sonar-git-plugin/src/main/java/org/sonar/plugins/scm/git/package-info.java25
-rw-r--r--plugins/sonar-git-plugin/src/test/java/org/sonar/plugins/scm/git/GitPluginTest.java32
-rw-r--r--plugins/sonar-git-plugin/src/test/java/org/sonar/plugins/scm/git/medium/GitMediumTest.java119
7 files changed, 574 insertions, 0 deletions
diff --git a/plugins/sonar-git-plugin/pom.xml b/plugins/sonar-git-plugin/pom.xml
new file mode 100644
index 00000000000..ef707db2795
--- /dev/null
+++ b/plugins/sonar-git-plugin/pom.xml
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.codehaus.sonar</groupId>
+ <artifactId>sonar</artifactId>
+ <version>5.0-SNAPSHOT</version>
+ <relativePath>../..</relativePath>
+ </parent>
+ <groupId>org.codehaus.sonar.plugins</groupId>
+ <artifactId>sonar-git-plugin</artifactId>
+ <name>SonarQube :: Plugins :: Git</name>
+ <packaging>sonar-plugin</packaging>
+ <description>Git SCM Provider.</description>
+
+ <dependencies>
+ <dependency>
+ <groupId>com.google.code.findbugs</groupId>
+ <artifactId>jsr305</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.codehaus.sonar</groupId>
+ <artifactId>sonar-plugin-api</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <!-- unit tests -->
+ <dependency>
+ <groupId>org.codehaus.sonar</groupId>
+ <artifactId>sonar-testing-harness</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.hamcrest</groupId>
+ <artifactId>hamcrest-all</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-core</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.codehaus.sonar</groupId>
+ <artifactId>sonar-batch</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ <exclusions>
+ <exclusion>
+ <artifactId>sonar-deprecated</artifactId>
+ <groupId>org.codehaus.sonar</groupId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>org.codehaus.sonar.plugins</groupId>
+ <artifactId>sonar-xoo-plugin</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.codehaus.sonar</groupId>
+ <artifactId>sonar-packaging-maven-plugin</artifactId>
+ <configuration>
+ <pluginKey>git</pluginKey>
+ <pluginName>Git</pluginName>
+ <pluginClass>org.sonar.plugins.scm.git.GitPlugin</pluginClass>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/plugins/sonar-git-plugin/src/main/java/org/sonar/plugins/scm/git/GitPlugin.java b/plugins/sonar-git-plugin/src/main/java/org/sonar/plugins/scm/git/GitPlugin.java
new file mode 100644
index 00000000000..033cef7b7de
--- /dev/null
+++ b/plugins/sonar-git-plugin/src/main/java/org/sonar/plugins/scm/git/GitPlugin.java
@@ -0,0 +1,34 @@
+/*
+ * 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 com.google.common.collect.ImmutableList;
+import org.sonar.api.SonarPlugin;
+
+import java.util.List;
+
+public final class GitPlugin extends SonarPlugin {
+
+ public List getExtensions() {
+ return ImmutableList.of(
+ GitScmProvider.class);
+ }
+
+}
diff --git a/plugins/sonar-git-plugin/src/main/java/org/sonar/plugins/scm/git/GitScmProvider.java b/plugins/sonar-git-plugin/src/main/java/org/sonar/plugins/scm/git/GitScmProvider.java
new file mode 100644
index 00000000000..7fbfe516c20
--- /dev/null
+++ b/plugins/sonar-git-plugin/src/main/java/org/sonar/plugins/scm/git/GitScmProvider.java
@@ -0,0 +1,97 @@
+/*
+ * 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.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 implements ScmProvider {
+
+ private static final Logger LOG = LoggerFactory.getLogger(GitScmProvider.class);
+
+ @Override
+ public String key() {
+ return "git";
+ }
+
+ @Override
+ public boolean supports(File baseDir) {
+ return new File(baseDir, ".git").exists();
+ }
+
+ @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);
+ 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();
+ }
+ }
+
+}
diff --git a/plugins/sonar-git-plugin/src/main/java/org/sonar/plugins/scm/git/SonarGitBlameConsumer.java b/plugins/sonar-git-plugin/src/main/java/org/sonar/plugins/scm/git/SonarGitBlameConsumer.java
new file mode 100644
index 00000000000..3102b00f105
--- /dev/null
+++ b/plugins/sonar-git-plugin/src/main/java/org/sonar/plugins/scm/git/SonarGitBlameConsumer.java
@@ -0,0 +1,190 @@
+/*
+ * Sonar SCM Activity Plugin
+ * Copyright (C) 2010 SonarSource
+ * dev@sonar.codehaus.org
+ *
+ * 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 02
+ */
+
+package org.sonar.plugins.scm.git;
+
+import com.google.common.annotations.VisibleForTesting;
+import org.slf4j.Logger;
+import org.sonar.api.batch.scm.BlameLine;
+import org.sonar.api.utils.command.StreamConsumer;
+
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Plain copy of package org.apache.maven.scm.provider.git.gitexe.command.blame.GitBlameConsumer
+ * Patched to allow user email retrieval when parsing Git blame results.
+ *
+ * @Todo: hack - to be submitted as an update in maven-scm-api for a future release
+ *
+ * <p/>
+ * For more information, see:
+ * <a href="http://jira.sonarsource.com/browse/DEVACT-103">DEVACT-103</a>
+ *
+ * @since 1.5.1
+ */
+public class SonarGitBlameConsumer implements StreamConsumer {
+
+ private static final String GIT_COMMITTER_PREFIX = "committer";
+ private static final String GIT_COMMITTER_TIME = GIT_COMMITTER_PREFIX + "-time ";
+ private static final String GIT_AUTHOR_EMAIL = "author-mail ";
+ private static final String GIT_COMMITTER_EMAIL = GIT_COMMITTER_PREFIX + "-mail ";
+ private static final String OPENING_EMAIL_FIELD = "<";
+ private static final String CLOSING_EMAIL_FIELD = ">";
+
+ private List<BlameLine> lines = new ArrayList<BlameLine>();
+
+ /**
+ * Since the porcelain format only contains the commit information
+ * the first time a specific sha-1 commit appears, we need to store
+ * this information somwehere.
+ * <p/>
+ * key: the sha-1 of the commit
+ * value: the {@link BlameLine} containing the full committer/author info
+ */
+ private Map<String, BlameLine> commitInfo = new HashMap<String, BlameLine>();
+
+ private boolean expectRevisionLine = true;
+
+ private String revision = null;
+ private String author = null;
+ private String committer = null;
+ private Date time = null;
+ private Logger logger;
+
+ public Logger getLogger() {
+ return logger;
+ }
+
+ public SonarGitBlameConsumer(Logger logger) {
+ this.logger = logger;
+ }
+
+ public void consumeLine(String line) {
+ if (line == null) {
+ return;
+ }
+
+ if (expectRevisionLine) {
+ // this is the revision line
+ consumeRevisionLine(line);
+ } else {
+
+ if (extractCommitInfoFromLine(line)) {
+ return;
+ }
+
+ if (line.startsWith("\t")) {
+ // this is the content line.
+ // we actually don't need the content, but this is the right time to add the blame line
+ consumeContentLine();
+ }
+ }
+ }
+
+ @VisibleForTesting
+ protected boolean extractCommitInfoFromLine(String line) {
+ if (line.startsWith(GIT_AUTHOR_EMAIL)) {
+ author = extractEmail(line);
+ return true;
+ }
+
+ if (line.startsWith(GIT_COMMITTER_EMAIL)) {
+ committer = extractEmail(line);
+ return true;
+ }
+
+ if (line.startsWith(GIT_COMMITTER_TIME)) {
+ String timeStr = line.substring(GIT_COMMITTER_TIME.length());
+ time = new Date(Long.parseLong(timeStr) * 1000L);
+ return true;
+ }
+ return false;
+ }
+
+ @VisibleForTesting
+ protected String getAuthor() {
+ return author;
+ }
+
+ @VisibleForTesting
+ protected String getCommitter() {
+ return committer;
+ }
+
+ @VisibleForTesting
+ protected Date getTime() {
+ return time;
+ }
+
+ private String extractEmail(String line) {
+
+ int emailStartIndex = line.indexOf(OPENING_EMAIL_FIELD);
+ int emailEndIndex = line.indexOf(CLOSING_EMAIL_FIELD);
+
+ if (emailStartIndex == -1 || emailEndIndex == -1 || emailEndIndex <= emailStartIndex) {
+ return null;
+ }
+ return line.substring(emailStartIndex + 1, emailEndIndex);
+ }
+
+ private void consumeContentLine() {
+ BlameLine blameLine = new BlameLine(time, revision, author, committer);
+ getLines().add(blameLine);
+
+ // keep commitinfo for this sha-1
+ commitInfo.put(revision, blameLine);
+
+ if (getLogger().isDebugEnabled()) {
+ DateFormat df = SimpleDateFormat.getDateTimeInstance();
+ getLogger().debug(author + " " + df.format(time));
+ }
+
+ expectRevisionLine = true;
+ }
+
+ private void consumeRevisionLine(String line) {
+ String[] parts = line.split("\\s", 4);
+
+ if (parts.length >= 1) {
+ revision = parts[0];
+
+ BlameLine oldLine = commitInfo.get(revision);
+
+ if (oldLine != null) {
+ // restore the commit info
+ author = oldLine.getAuthor();
+ committer = oldLine.getCommitter();
+ time = oldLine.getDate();
+ }
+
+ expectRevisionLine = false;
+ }
+ }
+
+ public List<BlameLine> getLines() {
+ return lines;
+ }
+}
diff --git a/plugins/sonar-git-plugin/src/main/java/org/sonar/plugins/scm/git/package-info.java b/plugins/sonar-git-plugin/src/main/java/org/sonar/plugins/scm/git/package-info.java
new file mode 100644
index 00000000000..748474dc689
--- /dev/null
+++ b/plugins/sonar-git-plugin/src/main/java/org/sonar/plugins/scm/git/package-info.java
@@ -0,0 +1,25 @@
+/*
+ * 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.plugins.scm.git;
+
+import javax.annotation.ParametersAreNonnullByDefault;
+
diff --git a/plugins/sonar-git-plugin/src/test/java/org/sonar/plugins/scm/git/GitPluginTest.java b/plugins/sonar-git-plugin/src/test/java/org/sonar/plugins/scm/git/GitPluginTest.java
new file mode 100644
index 00000000000..d65e0eb5844
--- /dev/null
+++ b/plugins/sonar-git-plugin/src/test/java/org/sonar/plugins/scm/git/GitPluginTest.java
@@ -0,0 +1,32 @@
+/*
+ * 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.Test;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+public class GitPluginTest {
+
+ @Test
+ public void getExtensions() {
+ assertThat(new GitPlugin().getExtensions()).hasSize(1);
+ }
+}
diff --git a/plugins/sonar-git-plugin/src/test/java/org/sonar/plugins/scm/git/medium/GitMediumTest.java b/plugins/sonar-git-plugin/src/test/java/org/sonar/plugins/scm/git/medium/GitMediumTest.java
new file mode 100644
index 00000000000..a1f34593f1b
--- /dev/null
+++ b/plugins/sonar-git-plugin/src/test/java/org/sonar/plugins/scm/git/medium/GitMediumTest.java
@@ -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);
+ }
+
+}