aboutsummaryrefslogtreecommitdiffstats
path: root/sonar-duplications
diff options
context:
space:
mode:
authorEvgeny Mandrikov <mandrikov@gmail.com>2011-12-08 09:49:04 +0400
committerEvgeny Mandrikov <mandrikov@gmail.com>2011-12-08 11:36:16 +0400
commita96994a5842aa65cf73111d00d25c6e92b3be0a1 (patch)
treea044891f8ec5cfd7ab3ae48bfc02dd40ae9c5953 /sonar-duplications
parentb34960b90f6376697d4d5aaf79d6050b2874848f (diff)
downloadsonarqube-a96994a5842aa65cf73111d00d25c6e92b3be0a1.tar.gz
sonarqube-a96994a5842aa65cf73111d00d25c6e92b3be0a1.zip
Add couple of functional tests for CPD
Diffstat (limited to 'sonar-duplications')
-rw-r--r--sonar-duplications/src/test/java/org/sonar/duplications/java/JavaDuplicationsFunctionalTest.java151
1 files changed, 144 insertions, 7 deletions
diff --git a/sonar-duplications/src/test/java/org/sonar/duplications/java/JavaDuplicationsFunctionalTest.java b/sonar-duplications/src/test/java/org/sonar/duplications/java/JavaDuplicationsFunctionalTest.java
index c9a1c1b90c7..dd85d6adcb5 100644
--- a/sonar-duplications/src/test/java/org/sonar/duplications/java/JavaDuplicationsFunctionalTest.java
+++ b/sonar-duplications/src/test/java/org/sonar/duplications/java/JavaDuplicationsFunctionalTest.java
@@ -22,13 +22,16 @@ package org.sonar.duplications.java;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
+import java.util.Collection;
import java.util.List;
import org.junit.Test;
import org.sonar.duplications.block.Block;
import org.sonar.duplications.block.BlockChunker;
-import org.sonar.duplications.detector.original.OriginalCloneDetectionAlgorithm;
+import org.sonar.duplications.detector.suffixtree.SuffixTreeCloneDetectionAlgorithm;
import org.sonar.duplications.index.CloneGroup;
+import org.sonar.duplications.index.CloneIndex;
+import org.sonar.duplications.index.ClonePart;
import org.sonar.duplications.index.MemoryCloneIndex;
import org.sonar.duplications.statement.Statement;
import org.sonar.duplications.statement.StatementChunker;
@@ -36,23 +39,157 @@ import org.sonar.duplications.token.TokenChunker;
import com.google.common.base.Joiner;
+/**
+ * From <a href="http://research.cs.queensu.ca/TechReports/Reports/2007-541.pdf">A Survey on Software Clone Detection Research (2007 year)</a>:
+ * <ul>
+ * <li>Type 1: Identical code fragments except for variations in whitespace (may be also variations in layout) and comments.
+ * Type 1 is widely know as Exact clones.</li>
+ * <li>Type 2: Structurally/syntactically identical fragments except for variations in identifiers, literals, types, layout and comments.
+ * The reserved words and the sentence structures are essentially the same.</li>
+ * <li>Type 3: Copied fragments with further modifications. Statements can be changed,
+ * added and/or deleted in addition to variations in identifiers, literals, types, layout
+ * and comments.</li>
+ * </ul>
+ */
public class JavaDuplicationsFunctionalTest {
+ @Test
+ public void type1() {
+ String fragment0 = source(
+ "if (a >= b) {",
+ " c = d + b; // Comment1",
+ " d = d + 1;}",
+ "else",
+ " c = d - a; // Comment2");
+ String fragment1 = source(
+ "if (a>=b) {",
+ " // Comment1",
+ " c=d+b;",
+ " d=d+1;",
+ "} else // Comment2",
+ " c=d-a;");
+ List<CloneGroup> duplications = detect2(fragment0, fragment1);
+ assertThat(duplications.size(), is(1));
+ ClonePart part = duplications.get(0).getOriginPart();
+ assertThat(part.getLineStart(), is(1));
+ assertThat(part.getLineEnd(), is(5));
+ }
+
+ /**
+ * Supports only subset of Type 2.
+ *
+ * @see #type2
+ * @see #literalsNormalization()
+ */
+ @Test
+ public void type2_literals() {
+ String fragment0 = source(
+ "if (a >= b) {",
+ " c = b + 1; // Comment1",
+ " d = '1';}",
+ "else",
+ " c = d - a; // Comment2");
+ String fragment1 = source(
+ "if (a >= b) {",
+ " c = b + 2; // Comment1",
+ " d = '2';}",
+ "else",
+ " c = d - a; // Comment2");
+ List<CloneGroup> duplications = detect2(fragment0, fragment1);
+ assertThat(duplications.size(), is(1));
+ ClonePart part = duplications.get(0).getOriginPart();
+ assertThat(part.getLineStart(), is(1));
+ assertThat(part.getLineEnd(), is(5));
+ }
+
+ @Test
+ public void type2() {
+ String fragment0 = source(
+ "if (a >= b) {",
+ " c = d + b; // Comment1",
+ " d = d + 1;}",
+ "else",
+ " c = d - a; // Comment2");
+ String fragment1 = source(
+ "if (m >= n) {",
+ " // Comment3",
+ " y = x + n; // Comment1",
+ " x = x + 5;}",
+ "else",
+ " y = x - m; // Comment2");
+ List<CloneGroup> duplications = detect2(fragment0, fragment1);
+ assertThat(duplications.size(), is(0));
+ }
+
+ /**
+ * Does not support Type 3, however able to detect inner parts.
+ */
+ @Test
+ public void type3() {
+ String fragment0 = source(
+ "public int getSoLinger() throws SocketException {",
+ " Object o = impl.getOption( SocketOptions.SO_LINGER);",
+ " if (o instanceof Integer) {",
+ " return((Integer) o).intValue();",
+ " }",
+ " else return -1;",
+ "}");
+ String fragment1 = source(
+ "public synchronized int getSoTimeout() throws SocketException {",
+ " Object o = impl.getOption( SocketOptions.SO_TIMEOUT);",
+ " if (o instanceof Integer) {",
+ " return((Integer) o).intValue();",
+ " }",
+ " else return -0;",
+ "}"
+ );
+ List<CloneGroup> duplications = detect2(fragment0, fragment1);
+ assertThat(duplications.size(), is(1));
+ ClonePart part = duplications.get(0).getOriginPart();
+ assertThat(part.getLineStart(), is(3));
+ assertThat(part.getLineEnd(), is(6));
+ }
+
+ private String source(String... lines) {
+ return Joiner.on('\n').join(lines);
+ }
+
+ private static List<CloneGroup> detect2(String... fragments) {
+ MemoryCloneIndex index = new MemoryCloneIndex();
+ for (int i = 0; i < fragments.length; i++) {
+ addToIndex(index, "fragment" + i, fragments[i]);
+ }
+ return detect(index, index.getByResourceId("fragment0"));
+ }
+
+ private static void addToIndex(CloneIndex index, String resourceId, String sourceCode) {
+ List<Statement> statements = STATEMENT_CHUNKER.chunk(TOKEN_CHUNKER.chunk(sourceCode));
+ BlockChunker blockChunker = new BlockChunker(2);
+ List<Block> blocks = blockChunker.chunk(resourceId, statements);
+ for (Block block : blocks) {
+ index.insert(block);
+ }
+ }
+
+ private static List<CloneGroup> detect(CloneIndex index, Collection<Block> blocks) {
+ return SuffixTreeCloneDetectionAlgorithm.detect(index, blocks);
+ }
+
private static final int BLOCK_SIZE = 1;
- private TokenChunker tokenChunker = JavaTokenProducer.build();
- private StatementChunker statementChunker = JavaStatementBuilder.build();
- private BlockChunker blockChunker = new BlockChunker(BLOCK_SIZE);
+ private static TokenChunker TOKEN_CHUNKER = JavaTokenProducer.build();
+ private static StatementChunker STATEMENT_CHUNKER = JavaStatementBuilder.build();
+ private static BlockChunker BLOCK_CHUNKER = new BlockChunker(BLOCK_SIZE);
private List<CloneGroup> detect(String... lines) {
String sourceCode = Joiner.on('\n').join(lines);
MemoryCloneIndex index = new MemoryCloneIndex();
- List<Statement> statements = statementChunker.chunk(tokenChunker.chunk(sourceCode));
- List<Block> blocks = blockChunker.chunk("resourceId", statements);
+ List<Statement> statements = STATEMENT_CHUNKER.chunk(TOKEN_CHUNKER.chunk(sourceCode));
+ List<Block> blocks = BLOCK_CHUNKER.chunk("resourceId", statements);
for (Block block : blocks) {
index.insert(block);
}
- return OriginalCloneDetectionAlgorithm.detect(index, blocks);
+ return detect(index, blocks);
}
/**