diff options
author | Evgeny Mandrikov <mandrikov@gmail.com> | 2011-12-08 09:49:04 +0400 |
---|---|---|
committer | Evgeny Mandrikov <mandrikov@gmail.com> | 2011-12-08 11:36:16 +0400 |
commit | a96994a5842aa65cf73111d00d25c6e92b3be0a1 (patch) | |
tree | a044891f8ec5cfd7ab3ae48bfc02dd40ae9c5953 /sonar-duplications | |
parent | b34960b90f6376697d4d5aaf79d6050b2874848f (diff) | |
download | sonarqube-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.java | 151 |
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); } /** |