aboutsummaryrefslogtreecommitdiffstats
path: root/sonar-duplications/src/test/java/org/sonar/duplications
diff options
context:
space:
mode:
Diffstat (limited to 'sonar-duplications/src/test/java/org/sonar/duplications')
-rw-r--r--sonar-duplications/src/test/java/org/sonar/duplications/DuplicationsTestUtil.java32
-rw-r--r--sonar-duplications/src/test/java/org/sonar/duplications/block/BlockChunkerTest.java63
-rw-r--r--sonar-duplications/src/test/java/org/sonar/duplications/block/BlockChunkerTestCase.java145
-rw-r--r--sonar-duplications/src/test/java/org/sonar/duplications/block/BlockTest.java80
-rw-r--r--sonar-duplications/src/test/java/org/sonar/duplications/block/ByteArrayTest.java66
-rw-r--r--sonar-duplications/src/test/java/org/sonar/duplications/detector/original/BlocksGroupTest.java200
-rw-r--r--sonar-duplications/src/test/java/org/sonar/duplications/detector/original/FilterTest.java193
-rw-r--r--sonar-duplications/src/test/java/org/sonar/duplications/detector/original/OriginalCloneDetectionAlgorithmTest.java512
-rw-r--r--sonar-duplications/src/test/java/org/sonar/duplications/index/DataUtilsTest.java90
-rw-r--r--sonar-duplications/src/test/java/org/sonar/duplications/index/MemoryCloneIndexTest.java102
-rw-r--r--sonar-duplications/src/test/java/org/sonar/duplications/index/PackedMemoryCloneIndexTest.java116
-rw-r--r--sonar-duplications/src/test/java/org/sonar/duplications/java/JavaStatementBuilderTest.java160
-rw-r--r--sonar-duplications/src/test/java/org/sonar/duplications/java/JavaTokenProducerTest.java293
-rw-r--r--sonar-duplications/src/test/java/org/sonar/duplications/junit/TestNamePrinter.java32
-rw-r--r--sonar-duplications/src/test/java/org/sonar/duplications/statement/StatementChannelDisptacherTest.java70
-rw-r--r--sonar-duplications/src/test/java/org/sonar/duplications/statement/StatementChannelTest.java101
-rw-r--r--sonar-duplications/src/test/java/org/sonar/duplications/statement/StatementChunkerTest.java31
-rw-r--r--sonar-duplications/src/test/java/org/sonar/duplications/statement/StatementTest.java51
-rw-r--r--sonar-duplications/src/test/java/org/sonar/duplications/statement/TokenMatcherFactoryTest.java48
-rw-r--r--sonar-duplications/src/test/java/org/sonar/duplications/statement/matcher/AnyTokenMatcherTest.java53
-rw-r--r--sonar-duplications/src/test/java/org/sonar/duplications/statement/matcher/BridgeTokenMatcherTest.java106
-rw-r--r--sonar-duplications/src/test/java/org/sonar/duplications/statement/matcher/ExactTokenMatcherTest.java73
-rw-r--r--sonar-duplications/src/test/java/org/sonar/duplications/statement/matcher/ForgetLastTokenMatcherTest.java52
-rw-r--r--sonar-duplications/src/test/java/org/sonar/duplications/statement/matcher/OptTokenMatcherTest.java56
-rw-r--r--sonar-duplications/src/test/java/org/sonar/duplications/statement/matcher/UptoTokenMatcherTest.java106
-rw-r--r--sonar-duplications/src/test/java/org/sonar/duplications/token/BlackHoleTokenChannelTest.java44
-rw-r--r--sonar-duplications/src/test/java/org/sonar/duplications/token/TokenChannelTest.java92
-rw-r--r--sonar-duplications/src/test/java/org/sonar/duplications/token/TokenChunkerTest.java46
-rw-r--r--sonar-duplications/src/test/java/org/sonar/duplications/token/TokenQueueTest.java68
-rw-r--r--sonar-duplications/src/test/java/org/sonar/duplications/token/TokenTest.java49
-rw-r--r--sonar-duplications/src/test/java/org/sonar/duplications/utils/FastStringComparatorTest.java70
-rw-r--r--sonar-duplications/src/test/java/org/sonar/duplications/utils/SortedListsUtilsTest.java60
32 files changed, 3260 insertions, 0 deletions
diff --git a/sonar-duplications/src/test/java/org/sonar/duplications/DuplicationsTestUtil.java b/sonar-duplications/src/test/java/org/sonar/duplications/DuplicationsTestUtil.java
new file mode 100644
index 00000000000..c1a37bba1ae
--- /dev/null
+++ b/sonar-duplications/src/test/java/org/sonar/duplications/DuplicationsTestUtil.java
@@ -0,0 +1,32 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2011 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar 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.
+ *
+ * Sonar 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 Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.duplications;
+
+import java.io.File;
+
+public class DuplicationsTestUtil {
+
+ public static final File fileDir = new File("src/test/files/");
+
+ public static File findFile(String relativePathToFile) {
+ return new File(fileDir, relativePathToFile);
+ }
+
+}
diff --git a/sonar-duplications/src/test/java/org/sonar/duplications/block/BlockChunkerTest.java b/sonar-duplications/src/test/java/org/sonar/duplications/block/BlockChunkerTest.java
new file mode 100644
index 00000000000..548b8e0c436
--- /dev/null
+++ b/sonar-duplications/src/test/java/org/sonar/duplications/block/BlockChunkerTest.java
@@ -0,0 +1,63 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2011 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar 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.
+ *
+ * Sonar 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 Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.duplications.block;
+
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+
+import java.util.List;
+
+import org.junit.Test;
+import org.sonar.duplications.statement.Statement;
+
+public class BlockChunkerTest extends BlockChunkerTestCase {
+
+ @Override
+ protected BlockChunker createChunkerWithBlockSize(int blockSize) {
+ return new BlockChunker(blockSize);
+ }
+
+ /**
+ * Rolling hash must produce exactly the same values as without rolling behavior.
+ * Moreover those values must always be the same (without dependency on JDK).
+ */
+ @Test
+ public void shouldCalculateHashes() {
+ List<Statement> statements = createStatementsFromStrings("aaaaaa", "bbbbbb", "cccccc", "dddddd", "eeeeee");
+ BlockChunker blockChunker = createChunkerWithBlockSize(3);
+ List<Block> blocks = blockChunker.chunk("resource", statements);
+ assertThat(blocks.get(0).getBlockHash(), equalTo(hash("aaaaaa", "bbbbbb", "cccccc")));
+ assertThat(blocks.get(1).getBlockHash(), equalTo(hash("bbbbbb", "cccccc", "dddddd")));
+ assertThat(blocks.get(2).getBlockHash(), equalTo(hash("cccccc", "dddddd", "eeeeee")));
+ assertThat(blocks.get(0).getBlockHash().toString(), is("fffffeb6ae1af4c0"));
+ assertThat(blocks.get(1).getBlockHash().toString(), is("fffffebd8512d120"));
+ assertThat(blocks.get(2).getBlockHash().toString(), is("fffffec45c0aad80"));
+ }
+
+ private ByteArray hash(String... statements) {
+ long hash = 0;
+ for (String statement : statements) {
+ hash = hash * 31 + statement.hashCode();
+ }
+ return new ByteArray(hash);
+ }
+
+}
diff --git a/sonar-duplications/src/test/java/org/sonar/duplications/block/BlockChunkerTestCase.java b/sonar-duplications/src/test/java/org/sonar/duplications/block/BlockChunkerTestCase.java
new file mode 100644
index 00000000000..90413bb2287
--- /dev/null
+++ b/sonar-duplications/src/test/java/org/sonar/duplications/block/BlockChunkerTestCase.java
@@ -0,0 +1,145 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2011 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar 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.
+ *
+ * Sonar 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 Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.duplications.block;
+
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.not;
+import static org.hamcrest.Matchers.sameInstance;
+import static org.junit.Assert.assertThat;
+
+import java.util.Collections;
+import java.util.List;
+
+import org.junit.Test;
+import org.sonar.duplications.statement.Statement;
+
+import com.google.common.collect.Lists;
+
+/**
+ * Any implementation of {@link BlockChunker} should pass these test scenarios.
+ */
+public abstract class BlockChunkerTestCase {
+
+ /**
+ * Factory method.
+ */
+ protected abstract BlockChunker createChunkerWithBlockSize(int blockSize);
+
+ /**
+ * Given:
+ * <pre>
+ * String[][] data = {
+ * {"a", "a"},
+ * {"a", "a"},
+ * {"a"},
+ * {"a", "a"},
+ * {"a", "a"}
+ * };
+ *
+ * Statements (where L - literal, C - comma): "LCL", "C", "LCL", "C", "L", "C", "LCL", "C", "LCL"
+ * Block size is 5.
+ * First block: "LCL", "C", "LCL", "C", "L"
+ * Last block: "L", "C", "LCL", "C", "LCL"
+ * </pre>
+ * Expected: different hashes for first and last blocks
+ */
+ @Test
+ public void testSameChars() {
+ List<Statement> statements = createStatementsFromStrings("LCL", "C", "LCL", "C", "L", "C", "LCL", "C", "LCL");
+ BlockChunker chunker = createChunkerWithBlockSize(5);
+ List<Block> blocks = chunker.chunk("resource", statements);
+ assertThat("first and last block should have different hashes", blocks.get(0).getBlockHash(), not(equalTo(blocks.get(blocks.size() - 1).getBlockHash())));
+ }
+
+ /**
+ * TODO Godin: should we allow empty statements in general?
+ */
+ @Test
+ public void testEmptyStatements() {
+ List<Statement> statements = createStatementsFromStrings("1", "", "1", "1", "");
+ BlockChunker chunker = createChunkerWithBlockSize(3);
+ List<Block> blocks = chunker.chunk("resource", statements);
+ assertThat("first and last block should have different hashes", blocks.get(0).getBlockHash(), not(equalTo(blocks.get(blocks.size() - 1).getBlockHash())));
+ }
+
+ /**
+ * Given: 5 statements, block size is 3
+ * Expected: 4 blocks with correct index and with line numbers
+ */
+ @Test
+ public void shouldBuildBlocksFromStatements() {
+ List<Statement> statements = createStatementsFromStrings("1", "2", "3", "4", "5", "6");
+ BlockChunker chunker = createChunkerWithBlockSize(3);
+ List<Block> blocks = chunker.chunk("resource", statements);
+ assertThat(blocks.size(), is(4));
+ assertThat(blocks.get(0).getIndexInFile(), is(0));
+ assertThat(blocks.get(0).getFirstLineNumber(), is(0));
+ assertThat(blocks.get(0).getLastLineNumber(), is(2));
+ assertThat(blocks.get(1).getIndexInFile(), is(1));
+ assertThat(blocks.get(1).getFirstLineNumber(), is(1));
+ assertThat(blocks.get(1).getLastLineNumber(), is(3));
+ }
+
+ @Test
+ public void testHashes() {
+ List<Statement> statements = createStatementsFromStrings("1", "2", "1", "2");
+ BlockChunker chunker = createChunkerWithBlockSize(2);
+ List<Block> blocks = chunker.chunk("resource", statements);
+ assertThat("blocks 0 and 2 should have same hash", blocks.get(0).getBlockHash(), equalTo(blocks.get(2).getBlockHash()));
+ assertThat("blocks 0 and 1 should have different hash", blocks.get(0).getBlockHash(), not(equalTo(blocks.get(1).getBlockHash())));
+ }
+
+ /**
+ * Given: 0 statements
+ * Expected: 0 blocks
+ */
+ @Test
+ public void shouldNotBuildBlocksWhenNoStatements() {
+ List<Statement> statements = Collections.emptyList();
+ BlockChunker blockChunker = createChunkerWithBlockSize(2);
+ List<Block> blocks = blockChunker.chunk("resource", statements);
+ assertThat(blocks, sameInstance(Collections.EMPTY_LIST));
+ }
+
+ /**
+ * Given: 1 statement, block size is 2
+ * Expected: 0 blocks
+ */
+ @Test
+ public void shouldNotBuildBlocksWhenNotEnoughStatements() {
+ List<Statement> statements = createStatementsFromStrings("statement");
+ BlockChunker blockChunker = createChunkerWithBlockSize(2);
+ List<Block> blocks = blockChunker.chunk("resource", statements);
+ assertThat(blocks, sameInstance(Collections.EMPTY_LIST));
+ }
+
+ /**
+ * Creates list of statements from Strings, each statement on a new line starting from 0.
+ */
+ protected static List<Statement> createStatementsFromStrings(String... values) {
+ List<Statement> result = Lists.newArrayList();
+ for (int i = 0; i < values.length; i++) {
+ result.add(new Statement(i, i, values[i]));
+ }
+ return result;
+ }
+
+}
diff --git a/sonar-duplications/src/test/java/org/sonar/duplications/block/BlockTest.java b/sonar-duplications/src/test/java/org/sonar/duplications/block/BlockTest.java
new file mode 100644
index 00000000000..3aeba301d5a
--- /dev/null
+++ b/sonar-duplications/src/test/java/org/sonar/duplications/block/BlockTest.java
@@ -0,0 +1,80 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2011 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar 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.
+ *
+ * Sonar 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 Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.duplications.block;
+
+import org.junit.Test;
+
+import static org.hamcrest.Matchers.*;
+import static org.junit.Assert.*;
+
+public class BlockTest {
+
+ @Test
+ public void fieldsTest() {
+ String fileName = "someFile";
+ int statementIndex = 4;
+ ByteArray hash = new ByteArray(12345);
+ Block tuple = new Block(fileName, hash, statementIndex, 0, 10);
+ assertThat(tuple.getResourceId(), equalTo(fileName));
+ assertThat(tuple.getIndexInFile(), equalTo(statementIndex));
+ assertEquals(tuple.getBlockHash(), hash);
+ }
+
+ @Test
+ public void tupleEqualsTest() {
+ Block tuple1 = new Block("somefile", new ByteArray(123), 1, 1, 10);
+ Block tuple2 = new Block("somefile", new ByteArray(123), 1, 1, 10);
+ Block tupleArr = new Block("somefile", new ByteArray(333), 1, 1, 10);
+ Block tupleIndex = new Block("somefile", new ByteArray(123), 2, 1, 10);
+ Block tupleName = new Block("other", new ByteArray(123), 1, 1, 10);
+
+ assertTrue(tuple1.equals(tuple2));
+ assertThat(tuple1.toString(), is(tuple2.toString()));
+
+ assertFalse(tuple1.equals(tupleArr));
+ assertThat(tuple1.toString(), not(equalTo(tupleArr.toString())));
+
+ assertFalse(tuple1.equals(tupleIndex));
+ assertThat(tuple1.toString(), not(equalTo(tupleIndex.toString())));
+
+ assertFalse(tuple1.equals(tupleName));
+ assertThat(tuple1.toString(), not(equalTo(tupleName.toString())));
+ }
+
+ @Test
+ public void hashCodeTest() {
+ String[] files = {"file1", "file2"};
+ int[] unitIndexes = {1, 2};
+ ByteArray[] arrays = {new ByteArray(123), new ByteArray(321)};
+
+ // fileName is in hashCode()
+ int defaultTupleHashCode = new Block(files[0], arrays[0], unitIndexes[0], 1, 10).hashCode();
+ int fileNameTupleHashCode = new Block(files[1], arrays[0], unitIndexes[0], 1, 10).hashCode();
+ assertThat(defaultTupleHashCode, not(equalTo(fileNameTupleHashCode)));
+
+ // statementIndex is in hashCode()
+ int indexTupleHashCode = new Block(files[0], arrays[0], unitIndexes[1], 1, 10).hashCode();
+ assertThat(defaultTupleHashCode, not(equalTo(indexTupleHashCode)));
+
+ // sequenceHash is in hashCode()
+ int sequenceHashTupleHashCode = new Block(files[0], arrays[1], unitIndexes[0], 1, 10).hashCode();
+ assertThat(defaultTupleHashCode, not(equalTo(sequenceHashTupleHashCode)));
+ }
+}
diff --git a/sonar-duplications/src/test/java/org/sonar/duplications/block/ByteArrayTest.java b/sonar-duplications/src/test/java/org/sonar/duplications/block/ByteArrayTest.java
new file mode 100644
index 00000000000..c237439f42e
--- /dev/null
+++ b/sonar-duplications/src/test/java/org/sonar/duplications/block/ByteArrayTest.java
@@ -0,0 +1,66 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2011 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar 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.
+ *
+ * Sonar 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 Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.duplications.block;
+
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+
+import org.junit.Test;
+
+public class ByteArrayTest {
+
+ @Test
+ public void shouldCreateFromInt() {
+ int value = 0x12FF8413;
+ ByteArray byteArray = new ByteArray(value);
+ assertThat(byteArray.toString(), is(Integer.toHexString(value)));
+ }
+
+ @Test
+ public void shouldCreateFromLong() {
+ long value = 0x12FF841344567899L;
+ ByteArray byteArray = new ByteArray(value);
+ assertThat(byteArray.toString(), is(Long.toHexString(value)));
+ }
+
+ @Test
+ public void shouldCreateFromHexString() {
+ String value = "12FF841344567899";
+ ByteArray byteArray = new ByteArray(value);
+ assertThat(byteArray.toString(), is(value.toLowerCase()));
+ }
+
+ @Test
+ public void shouldCreateFromIntArray() {
+ ByteArray byteArray = new ByteArray(new int[] { 0x04121986 });
+ assertThat(byteArray.toString(), is("04121986"));
+ }
+
+ @Test
+ public void shouldConvertToIntArray() {
+ // number of bytes is enough to create exactly one int (4 bytes)
+ ByteArray byteArray = new ByteArray(new byte[] { 0x04, 0x12, 0x19, (byte) 0x86 });
+ assertThat(byteArray.toIntArray(), is(new int[] { 0x04121986 }));
+ // number of bytes is more than 4, but less than 8, so anyway 2 ints
+ byteArray = new ByteArray(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x31 });
+ assertThat(byteArray.toIntArray(), is(new int[] { 0x00000000, 0x31000000 }));
+ }
+
+}
diff --git a/sonar-duplications/src/test/java/org/sonar/duplications/detector/original/BlocksGroupTest.java b/sonar-duplications/src/test/java/org/sonar/duplications/detector/original/BlocksGroupTest.java
new file mode 100644
index 00000000000..a8c44f67784
--- /dev/null
+++ b/sonar-duplications/src/test/java/org/sonar/duplications/detector/original/BlocksGroupTest.java
@@ -0,0 +1,200 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2011 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar 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.
+ *
+ * Sonar 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 Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.duplications.detector.original;
+
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+
+import org.junit.Test;
+import org.sonar.duplications.block.Block;
+
+public class BlocksGroupTest {
+
+ /**
+ * {@link BlocksGroup} uses only resourceId and index from block, thus we can simplify testing.
+ */
+ private static Block newBlock(String resourceId, int indexInFile) {
+ return new Block(resourceId, null, indexInFile, indexInFile, indexInFile);
+ }
+
+ public static BlocksGroup newBlocksGroup(Block... blocks) {
+ BlocksGroup result = BlocksGroup.empty();
+ for (Block block : blocks) {
+ result.blocks.add(block);
+ }
+ return result;
+ }
+
+ @Test
+ public void shouldReturnSize() {
+ BlocksGroup group = newBlocksGroup(newBlock("a", 1), newBlock("b", 2));
+ assertThat(group.size(), is(2));
+ }
+
+ @Test
+ public void shouldCreateEmptyGroup() {
+ assertThat(BlocksGroup.empty().size(), is(0));
+ }
+
+ @Test
+ public void testSubsumedBy() {
+ BlocksGroup group1 = newBlocksGroup(newBlock("a", 1), newBlock("b", 2));
+ BlocksGroup group2 = newBlocksGroup(newBlock("a", 2), newBlock("b", 3), newBlock("c", 4));
+ // block "c" from group2 does not have corresponding block in group1
+ assertThat(group2.subsumedBy(group1, 1), is(false));
+ }
+
+ @Test
+ public void testSubsumedBy2() {
+ BlocksGroup group1 = newBlocksGroup(newBlock("a", 1), newBlock("b", 2));
+ BlocksGroup group2 = newBlocksGroup(newBlock("a", 2), newBlock("b", 3));
+ BlocksGroup group3 = newBlocksGroup(newBlock("a", 3), newBlock("b", 4));
+ BlocksGroup group4 = newBlocksGroup(newBlock("a", 4), newBlock("b", 5));
+
+ assertThat(group2.subsumedBy(group1, 1), is(true)); // correction of index - 1
+
+ assertThat(group3.subsumedBy(group1, 2), is(true)); // correction of index - 2
+ assertThat(group3.subsumedBy(group2, 1), is(true)); // correction of index - 1
+
+ assertThat(group4.subsumedBy(group1, 3), is(true)); // correction of index - 3
+ assertThat(group4.subsumedBy(group2, 2), is(true)); // correction of index - 2
+ assertThat(group4.subsumedBy(group3, 1), is(true)); // correction of index - 1
+ }
+
+ @Test
+ public void testIntersect() {
+ BlocksGroup group1 = newBlocksGroup(newBlock("a", 1), newBlock("b", 2));
+ BlocksGroup group2 = newBlocksGroup(newBlock("a", 2), newBlock("b", 3));
+ BlocksGroup intersection = group1.intersect(group2);
+ assertThat(intersection.size(), is(2));
+ }
+
+ /**
+ * Results for this test taken from results of work of naive implementation.
+ */
+ @Test
+ public void testSubsumedBy3() {
+ // ['a'[2|2-7]:3, 'b'[0|0-5]:3] subsumedBy ['a'[1|1-6]:2] false
+ assertThat(newBlocksGroup(newBlock("a", 2), newBlock("b", 0))
+ .subsumedBy(newBlocksGroup(newBlock("a", 1)), 1),
+ is(false));
+
+ // ['a'[3|3-8]:4, 'b'[1|1-6]:4] subsumedBy ['a'[1|1-6]:2] false
+ assertThat(newBlocksGroup(newBlock("a", 3), newBlock("b", 1))
+ .subsumedBy(newBlocksGroup(newBlock("a", 1)), 1),
+ is(false));
+
+ // ['a'[4|4-9]:5, 'b'[2|2-7]:5] subsumedBy ['a'[1|1-6]:2] false
+ assertThat(newBlocksGroup(newBlock("a", 4), newBlock("b", 2))
+ .subsumedBy(newBlocksGroup(newBlock("a", 1)), 1),
+ is(false));
+
+ // ['a'[5|5-10]:6, 'b'[3|3-8]:6] subsumedBy ['a'[1|1-6]:2] false
+ assertThat(newBlocksGroup(newBlock("a", 5), newBlock("b", 3))
+ .subsumedBy(newBlocksGroup(newBlock("a", 1)), 1),
+ is(false));
+
+ // ['a'[3|3-8]:4, 'b'[1|1-6]:4] subsumedBy ['a'[2|2-7]:3, 'b'[0|0-5]:3] true
+ assertThat(newBlocksGroup(newBlock("a", 3), newBlock("b", 1))
+ .subsumedBy(newBlocksGroup(newBlock("a", 2), newBlock("b", 0)), 1),
+ is(true));
+
+ // ['a'[4|4-9]:5, 'b'[2|2-7]:5, 'c'[0|0-5]:5] subsumedBy ['a'[3|3-8]:4, 'b'[1|1-6]:4] false
+ assertThat(newBlocksGroup(newBlock("a", 4), newBlock("b", 2), newBlock("c", 0))
+ .subsumedBy(newBlocksGroup(newBlock("a", 3), newBlock("b", 1)), 1),
+ is(false));
+
+ // ['a'[5|5-10]:6, 'b'[3|3-8]:6, 'c'[1|1-6]:6] subsumedBy ['a'[3|3-8]:4, 'b'[1|1-6]:4] false
+ assertThat(newBlocksGroup(newBlock("a", 5), newBlock("b", 3), newBlock("c", 1))
+ .subsumedBy(newBlocksGroup(newBlock("a", 3), newBlock("b", 1)), 1),
+ is(false));
+
+ // ['a'[6|6-11]:7, 'c'[2|2-7]:7] subsumedBy ['a'[3|3-8]:4, 'b'[1|1-6]:4] false
+ assertThat(newBlocksGroup(newBlock("a", 6), newBlock("c", 2))
+ .subsumedBy(newBlocksGroup(newBlock("a", 3), newBlock("b", 1)), 1),
+ is(false));
+
+ // ['a'[5|5-10]:6, 'b'[3|3-8]:6, 'c'[1|1-6]:6] subsumedBy ['a'[4|4-9]:5, 'b'[2|2-7]:5, 'c'[0|0-5]:5] true
+ assertThat(newBlocksGroup(newBlock("a", 5), newBlock("b", 3), newBlock("c", 1))
+ .subsumedBy(newBlocksGroup(newBlock("a", 4), newBlock("b", 2), newBlock("c", 0)), 1),
+ is(true));
+
+ // ['a'[6|6-11]:7, 'c'[2|2-7]:7] subsumedBy ['a'[5|5-10]:6, 'b'[3|3-8]:6, 'c'[1|1-6]:6] true
+ assertThat(newBlocksGroup(newBlock("a", 6), newBlock("c", 2))
+ .subsumedBy(newBlocksGroup(newBlock("a", 5), newBlock("b", 3), newBlock("c", 1)), 1),
+ is(true));
+ }
+
+ /**
+ * Results for this test taken from results of work of naive implementation.
+ */
+ @Test
+ public void testIntersect2() {
+ // ['a'[2|2-7]:3, 'b'[0|0-5]:3]
+ // intersect ['a'[3|3-8]:4, 'b'[1|1-6]:4]
+ // as ['a'[3|3-8]:4, 'b'[1|1-6]:4]
+ assertThat(newBlocksGroup(newBlock("a", 2), newBlock("b", 0))
+ .intersect(newBlocksGroup(newBlock("a", 3), newBlock("b", 1)))
+ .size(), is(2));
+
+ // ['a'[3|3-8]:4, 'b'[1|1-6]:4]
+ // intersect ['a'[4|4-9]:5, 'b'[2|2-7]:5, 'c'[0|0-5]:5]
+ // as ['a'[4|4-9]:5, 'b'[2|2-7]:5]
+ assertThat(newBlocksGroup(newBlock("a", 3), newBlock("b", 1))
+ .intersect(newBlocksGroup(newBlock("a", 4), newBlock("b", 2), newBlock("c", 0)))
+ .size(), is(2));
+
+ // ['a'[4|4-9]:5, 'b'[2|2-7]:5]
+ // intersect ['a'[5|5-10]:6, 'b'[3|3-8]:6, 'c'[1|1-6]:6]
+ // as ['a'[5|5-10]:6, 'b'[3|3-8]:6]
+ assertThat(newBlocksGroup(newBlock("a", 4), newBlock("b", 2))
+ .intersect(newBlocksGroup(newBlock("a", 5), newBlock("b", 3), newBlock("c", 1)))
+ .size(), is(2));
+
+ // ['a'[5|5-10]:6, 'b'[3|3-8]:6]
+ // intersect ['a'[6|6-11]:7, 'c'[2|2-7]:7]
+ // as ['a'[6|6-11]:7]
+ assertThat(newBlocksGroup(newBlock("a", 5), newBlock("b", 3))
+ .intersect(newBlocksGroup(newBlock("a", 6), newBlock("c", 2)))
+ .size(), is(1));
+
+ // ['a'[4|4-9]:5, 'b'[2|2-7]:5, 'c'[0|0-5]:5]
+ // intersect ['a'[5|5-10]:6, 'b'[3|3-8]:6, 'c'[1|1-6]:6]
+ // as ['a'[5|5-10]:6, 'b'[3|3-8]:6, 'c'[1|1-6]:6]
+ assertThat(newBlocksGroup(newBlock("a", 4), newBlock("b", 2), newBlock("c", 0))
+ .intersect(newBlocksGroup(newBlock("a", 5), newBlock("b", 3), newBlock("c", 1)))
+ .size(), is(3));
+
+ // ['a'[5|5-10]:6, 'b'[3|3-8]:6, 'c'[1|1-6]:6]
+ // intersect ['a'[6|6-11]:7, 'c'[2|2-7]:7]
+ // as ['a'[6|6-11]:7, 'c'[2|2-7]:7]
+ assertThat(newBlocksGroup(newBlock("a", 5), newBlock("b", 3), newBlock("c", 1))
+ .intersect(newBlocksGroup(newBlock("a", 6), newBlock("c", 2)))
+ .size(), is(2));
+
+ // ['a'[6|6-11]:7, 'c'[2|2-7]:7]
+ // intersect ['a'[7|7-12]:8]
+ // as ['a'[7|7-12]:8]
+ assertThat(newBlocksGroup(newBlock("a", 6), newBlock("c", 7))
+ .intersect(newBlocksGroup(newBlock("a", 7)))
+ .size(), is(1));
+ }
+
+}
diff --git a/sonar-duplications/src/test/java/org/sonar/duplications/detector/original/FilterTest.java b/sonar-duplications/src/test/java/org/sonar/duplications/detector/original/FilterTest.java
new file mode 100644
index 00000000000..9b6198d345f
--- /dev/null
+++ b/sonar-duplications/src/test/java/org/sonar/duplications/detector/original/FilterTest.java
@@ -0,0 +1,193 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2011 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar 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.
+ *
+ * Sonar 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 Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.duplications.detector.original;
+
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+
+import java.util.Arrays;
+
+import org.junit.Test;
+import org.sonar.duplications.index.CloneGroup;
+import org.sonar.duplications.index.ClonePart;
+
+public class FilterTest {
+
+ /**
+ * Given:
+ * <pre>
+ * c1: a[1-1]
+ * c2: a[1-1]
+ * </pre>
+ * Expected:
+ * reflexive - c1 in c1,
+ * antisymmetric - c1 in c2, c2 in c1, because c1 = c2
+ */
+ @Test
+ public void reflexive_and_antisymmetric() {
+ CloneGroup c1 = newCloneGroup(1,
+ newClonePart("a", 1));
+ CloneGroup c2 = newCloneGroup(1,
+ newClonePart("a", 1));
+
+ assertThat(Filter.containsIn(c1, c1), is(true));
+ assertThat(Filter.containsIn(c1, c2), is(true));
+ assertThat(Filter.containsIn(c2, c1), is(true));
+ }
+
+ /**
+ * Given:
+ * <pre>
+ * c1: a[1-1]
+ * c2: a[2-2]
+ * </pre>
+ * Expected: c1 not in c2, c2 not in c1
+ */
+ @Test
+ public void start_index_in_C1_less_than_in_C2() {
+ CloneGroup c1 = newCloneGroup(1,
+ newClonePart("a", 1));
+ CloneGroup c2 = newCloneGroup(1,
+ newClonePart("a", 2));
+
+ assertThat(Filter.containsIn(c1, c2), is(false));
+ }
+
+ /**
+ * Given:
+ * <pre>
+ * c1: a[0-0], a[2-2], b[0-0], b[2-2]
+ * c2: a[0-2], b[0-2]
+ * </pre>
+ * Expected:
+ * <pre>
+ * c1 in c2 (all parts of c1 covered by parts of c2 and all resources the same)
+ * c2 not in c1 (not all parts of c2 covered by parts of c1 and all resources the same)
+ * </pre>
+ */
+ @Test
+ public void one_part_of_C2_covers_two_parts_of_C1() {
+ // Note that line numbers don't matter for method which we test.
+ CloneGroup c1 = newCloneGroup(1,
+ newClonePart("a", 0),
+ newClonePart("a", 2),
+ newClonePart("b", 0),
+ newClonePart("b", 2));
+ CloneGroup c2 = newCloneGroup(3,
+ newClonePart("a", 0),
+ newClonePart("b", 0));
+
+ assertThat(Filter.containsIn(c1, c2), is(true));
+ assertThat(Filter.containsIn(c2, c1), is(false));
+ }
+
+ /**
+ * Given:
+ * <pre>
+ * c1: a[0-0], a[2-2]
+ * c2: a[0-2], b[0-2]
+ * </pre>
+ * Expected:
+ * <pre>
+ * c1 not in c2 (all parts of c1 covered by parts of c2, but different resources)
+ * c2 not in c1 (not all parts of c2 covered by parts of c1 and different resources)
+ * </pre>
+ */
+ @Test
+ public void different_resources() {
+ CloneGroup c1 = newCloneGroup(1,
+ newClonePart("a", 0),
+ newClonePart("a", 2));
+ CloneGroup c2 = newCloneGroup(3,
+ newClonePart("a", 0),
+ newClonePart("b", 0));
+
+ assertThat(Filter.containsIn(c1, c2), is(false));
+ assertThat(Filter.containsIn(c2, c1), is(false));
+ }
+
+ /**
+ * Given:
+ * <pre>
+ * c1: a[2-2]
+ * c2: a[0-1], a[2-3]
+ * </pre>
+ * Expected:
+ * <pre>
+ * c1 in c2
+ * c2 not in c1
+ * </pre>
+ */
+ @Test
+ public void second_part_of_C2_covers_first_part_of_C1() {
+ CloneGroup c1 = newCloneGroup(1,
+ newClonePart("a", 2));
+ CloneGroup c2 = newCloneGroup(2,
+ newClonePart("a", 0),
+ newClonePart("a", 2));
+
+ assertThat(Filter.containsIn(c1, c2), is(true));
+ assertThat(Filter.containsIn(c2, c1), is(false));
+ }
+
+ /**
+ * Given:
+ * <pre>
+ * c1: a[0-2]
+ * c2: a[0-0]
+ * </pre>
+ * Expected:
+ * <pre>
+ * c1 not in c2
+ * </pre>
+ */
+ @Test
+ public void length_of_C1_bigger_than_length_of_C2() {
+ CloneGroup c1 = spy(newCloneGroup(3,
+ newClonePart("a", 0)));
+ CloneGroup c2 = spy(newCloneGroup(1,
+ newClonePart("a", 0)));
+
+ assertThat(Filter.containsIn(c1, c2), is(false));
+ // containsIn method should check only origin and length - no need to compare all parts
+ verify(c1).getCloneUnitLength();
+ verify(c2).getCloneUnitLength();
+ verifyNoMoreInteractions(c1);
+ verifyNoMoreInteractions(c2);
+ }
+
+ /**
+ * Creates new part with specified resourceId and unitStart, and 0 for lineStart and lineEnd.
+ */
+ private ClonePart newClonePart(String resourceId, int unitStart) {
+ return new ClonePart(resourceId, unitStart, 0, 0);
+ }
+
+ /**
+ * Creates new group from list of parts, origin - is a first part from list.
+ */
+ private CloneGroup newCloneGroup(int len, ClonePart... parts) {
+ return new CloneGroup(len, parts[0], Arrays.asList(parts));
+ }
+
+}
diff --git a/sonar-duplications/src/test/java/org/sonar/duplications/detector/original/OriginalCloneDetectionAlgorithmTest.java b/sonar-duplications/src/test/java/org/sonar/duplications/detector/original/OriginalCloneDetectionAlgorithmTest.java
new file mode 100644
index 00000000000..e22faf753a9
--- /dev/null
+++ b/sonar-duplications/src/test/java/org/sonar/duplications/detector/original/OriginalCloneDetectionAlgorithmTest.java
@@ -0,0 +1,512 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2011 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar 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.
+ *
+ * Sonar 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 Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.duplications.detector.original;
+
+import static org.hamcrest.Matchers.hasItem;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.sameInstance;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.duplications.block.Block;
+import org.sonar.duplications.block.ByteArray;
+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.junit.TestNamePrinter;
+
+import com.google.common.collect.Lists;
+
+public class OriginalCloneDetectionAlgorithmTest {
+
+ @Rule
+ public TestNamePrinter name = new TestNamePrinter();
+
+ private static int LINES_PER_BLOCK = 5;
+
+ /**
+ * To simplify testing we assume that each block starts from a new line and contains {@link #LINES_PER_BLOCK} lines,
+ * so we can simply use index and hash.
+ */
+ private static Block newBlock(String resourceId, ByteArray hash, int index) {
+ return new Block(resourceId, hash, index, index, index + LINES_PER_BLOCK);
+ }
+
+ private static ClonePart newClonePart(String resourceId, int unitStart, int cloneUnitLength) {
+ return new ClonePart(resourceId, unitStart, unitStart, unitStart + cloneUnitLength + LINES_PER_BLOCK - 1);
+ }
+
+ /**
+ * Given:
+ * <pre>
+ * y: 2 3 4 5
+ * z: 3 4
+ * x: 1 2 3 4 5 6
+ * </pre>
+ * Expected:
+ * <pre>
+ * x-y (2 3 4 5)
+ * x-y-z (3 4)
+ * </pre>
+ */
+ @Test
+ public void exampleFromPaper() {
+ CloneIndex cloneIndex = createIndex(
+ blocksForResource("y").withHashes("2", "3", "4", "5"),
+ blocksForResource("z").withHashes("3", "4"));
+ List<Block> fileBlocks = blocksForResource("x").withHashes("1", "2", "3", "4", "5", "6");
+ List<CloneGroup> clones = OriginalCloneDetectionAlgorithm.detect(cloneIndex, fileBlocks);
+ print(clones);
+ assertThat(clones.size(), is(2));
+ Iterator<CloneGroup> clonesIterator = clones.iterator();
+
+ CloneGroup clone = clonesIterator.next();
+ assertThat(clone.getCloneUnitLength(), is(4));
+ assertThat(clone.getCloneParts().size(), is(2));
+ assertThat(clone.getOriginPart(), is(newClonePart("x", 1, 4)));
+ assertThat(clone.getCloneParts(), hasItem(newClonePart("x", 1, 4)));
+ assertThat(clone.getCloneParts(), hasItem(newClonePart("y", 0, 4)));
+
+ clone = clonesIterator.next();
+ assertThat(clone.getCloneUnitLength(), is(2));
+ assertThat(clone.getCloneParts().size(), is(3));
+ assertThat(clone.getOriginPart(), is(newClonePart("x", 2, 2)));
+ assertThat(clone.getCloneParts(), hasItem(newClonePart("x", 2, 2)));
+ assertThat(clone.getCloneParts(), hasItem(newClonePart("y", 1, 2)));
+ assertThat(clone.getCloneParts(), hasItem(newClonePart("z", 0, 2)));
+ }
+
+ /**
+ * Given:
+ * <pre>
+ * a: 2 3 4 5
+ * b: 3 4
+ * c: 1 2 3 4 5 6
+ * </pre>
+ * Expected:
+ * <pre>
+ * c-a (2 3 4 5)
+ * c-a-b (3 4)
+ * </pre>
+ */
+ @Test
+ public void exampleFromPaperWithModifiedResourceIds() {
+ CloneIndex cloneIndex = createIndex(
+ blocksForResource("a").withHashes("2", "3", "4", "5"),
+ blocksForResource("b").withHashes("3", "4"));
+ List<Block> fileBlocks = blocksForResource("c").withHashes("1", "2", "3", "4", "5", "6");
+ List<CloneGroup> clones = OriginalCloneDetectionAlgorithm.detect(cloneIndex, fileBlocks);
+ print(clones);
+ assertThat(clones.size(), is(2));
+ Iterator<CloneGroup> clonesIterator = clones.iterator();
+
+ CloneGroup clone = clonesIterator.next();
+ assertThat(clone.getCloneUnitLength(), is(4));
+ assertThat(clone.getCloneParts().size(), is(2));
+ assertThat(clone.getOriginPart(), is(newClonePart("c", 1, 4)));
+ assertThat(clone.getCloneParts(), hasItem(newClonePart("c", 1, 4)));
+ assertThat(clone.getCloneParts(), hasItem(newClonePart("a", 0, 4)));
+
+ clone = clonesIterator.next();
+ assertThat(clone.getCloneUnitLength(), is(2));
+ assertThat(clone.getCloneParts().size(), is(3));
+ assertThat(clone.getOriginPart(), is(newClonePart("c", 2, 2)));
+ assertThat(clone.getCloneParts(), hasItem(newClonePart("c", 2, 2)));
+ assertThat(clone.getCloneParts(), hasItem(newClonePart("a", 1, 2)));
+ assertThat(clone.getCloneParts(), hasItem(newClonePart("b", 0, 2)));
+ }
+
+ /**
+ * Given:
+ * <pre>
+ * b: 3 4 5 6
+ * c: 5 6 7
+ * a: 1 2 3 4 5 6 7 8 9
+ * </pre>
+ * Expected:
+ * <pre>
+ * a-b (3 4 5 6)
+ * a-b-c (5 6)
+ * a-c (5 6 7)
+ * </pre>
+ */
+ @Test
+ public void example1() {
+ CloneIndex cloneIndex = createIndex(
+ blocksForResource("b").withHashes("3", "4", "5", "6"),
+ blocksForResource("c").withHashes("5", "6", "7"));
+ List<Block> fileBlocks =
+ blocksForResource("a").withHashes("1", "2", "3", "4", "5", "6", "7", "8", "9");
+ List<CloneGroup> clones = OriginalCloneDetectionAlgorithm.detect(cloneIndex, fileBlocks);
+ print(clones);
+ assertThat(clones.size(), is(3));
+ Iterator<CloneGroup> clonesIterator = clones.iterator();
+
+ CloneGroup clone = clonesIterator.next();
+ assertThat(clone.getCloneUnitLength(), is(4));
+ assertThat(clone.getCloneParts().size(), is(2));
+ assertThat(clone.getOriginPart(), is(newClonePart("a", 2, 4)));
+ assertThat(clone.getCloneParts(), hasItem(newClonePart("a", 2, 4)));
+ assertThat(clone.getCloneParts(), hasItem(newClonePart("b", 0, 4)));
+
+ clone = clonesIterator.next();
+ assertThat(clone.getCloneUnitLength(), is(2));
+ assertThat(clone.getCloneParts().size(), is(3));
+ assertThat(clone.getOriginPart(), is(newClonePart("a", 4, 2)));
+ assertThat(clone.getCloneParts(), hasItem(newClonePart("a", 4, 2)));
+ assertThat(clone.getCloneParts(), hasItem(newClonePart("b", 2, 2)));
+ assertThat(clone.getCloneParts(), hasItem(newClonePart("c", 0, 2)));
+
+ clone = clonesIterator.next();
+ assertThat(clone.getCloneUnitLength(), is(3));
+ assertThat(clone.getCloneParts().size(), is(2));
+ assertThat(clone.getOriginPart(), is(newClonePart("a", 4, 3)));
+ assertThat(clone.getCloneParts(), hasItem(newClonePart("a", 4, 3)));
+ assertThat(clone.getCloneParts(), hasItem(newClonePart("c", 0, 3)));
+ }
+
+ /**
+ * Given:
+ * <pre>
+ * b: 1 2 3 4 1 2 3 4 1 2 3 4
+ * c: 1 2 3 4
+ * a: 1 2 3 4 5
+ * </pre>
+ * Expected:
+ * <pre>
+ * a-b-b-b-c (1 2 3 4)
+ * </pre>
+ */
+ @Test
+ public void example2() {
+ CloneIndex cloneIndex = createIndex(
+ blocksForResource("b").withHashes("1", "2", "3", "4", "1", "2", "3", "4", "1", "2", "3", "4"),
+ blocksForResource("c").withHashes("1", "2", "3", "4"));
+ List<Block> fileBlocks =
+ blocksForResource("a").withHashes("1", "2", "3", "5");
+ List<CloneGroup> clones = OriginalCloneDetectionAlgorithm.detect(cloneIndex, fileBlocks);
+ print(clones);
+ assertThat(clones.size(), is(1));
+ Iterator<CloneGroup> clonesIterator = clones.iterator();
+
+ CloneGroup clone = clonesIterator.next();
+ assertThat(clone.getCloneUnitLength(), is(3));
+ assertThat(clone.getCloneParts().size(), is(5));
+ assertThat(clone.getOriginPart(), is(newClonePart("a", 0, 3)));
+ assertThat(clone.getCloneParts(), hasItem(newClonePart("a", 0, 3)));
+ assertThat(clone.getCloneParts(), hasItem(newClonePart("b", 0, 3)));
+ assertThat(clone.getCloneParts(), hasItem(newClonePart("b", 4, 3)));
+ assertThat(clone.getCloneParts(), hasItem(newClonePart("b", 8, 3)));
+ assertThat(clone.getCloneParts(), hasItem(newClonePart("c", 0, 3)));
+ }
+
+ /**
+ * Given:
+ * <pre>
+ * b: 1 2 3 4
+ * a: 1 2 3
+ * </pre>
+ * Expected clone which ends at the end of file "a":
+ * <pre>
+ * a-b (1 2 3)
+ * </pre>
+ */
+ @Test
+ public void problemWithEndOfFile() {
+ CloneIndex cloneIndex = createIndex(
+ blocksForResource("b").withHashes("1", "2", "3", "4"));
+ List<Block> fileBlocks =
+ blocksForResource("a").withHashes("1", "2", "3");
+ List<CloneGroup> clones = OriginalCloneDetectionAlgorithm.detect(cloneIndex, fileBlocks);
+ print(clones);
+ assertThat(clones.size(), is(1));
+ Iterator<CloneGroup> clonesIterator = clones.iterator();
+
+ CloneGroup clone = clonesIterator.next();
+ assertThat(clone.getCloneUnitLength(), is(3));
+ assertThat(clone.getCloneParts().size(), is(2));
+ assertThat(clone.getOriginPart(), is(newClonePart("a", 0, 3)));
+ assertThat(clone.getCloneParts(), hasItem(newClonePart("a", 0, 3)));
+ assertThat(clone.getCloneParts(), hasItem(newClonePart("b", 0, 3)));
+ }
+
+ /**
+ * Test for problem, which was described in original paper - same clone would be reported twice.
+ * Given:
+ * <pre>
+ * a: 1 2 3 1 2 4
+ * </pre>
+ * Expected only one clone:
+ * <pre>
+ * a-a (1 2)
+ * </pre>
+ */
+ @Test
+ public void clonesInFileItself() {
+ CloneIndex cloneIndex = createIndex();
+ List<Block> fileBlocks =
+ blocksForResource("a").withHashes("1", "2", "3", "1", "2", "4");
+ List<CloneGroup> clones = OriginalCloneDetectionAlgorithm.detect(cloneIndex, fileBlocks);
+ print(clones);
+
+ assertThat(clones.size(), is(1));
+ Iterator<CloneGroup> clonesIterator = clones.iterator();
+
+ CloneGroup clone = clonesIterator.next();
+ assertThat(clone.getCloneUnitLength(), is(2));
+ assertThat(clone.getCloneParts().size(), is(2));
+ assertThat(clone.getOriginPart(), is(newClonePart("a", 0, 2)));
+ assertThat(clone.getCloneParts(), hasItem(newClonePart("a", 0, 2)));
+ assertThat(clone.getCloneParts(), hasItem(newClonePart("a", 3, 2)));
+ }
+
+ /**
+ * Given:
+ * <pre>
+ * b: 1 2 1 2
+ * a: 1 2 1
+ * </pre>
+ * Expected:
+ * <pre>
+ * a-b-b (1 2)
+ * a-b (1 2 1)
+ * </pre>
+ * "a-a-b-b (1)" should not be reported, because fully covered by "a-b (1 2 1)"
+ */
+ @Test
+ public void covered() {
+ CloneIndex cloneIndex = createIndex(
+ blocksForResource("b").withHashes("1", "2", "1", "2"));
+ List<Block> fileBlocks =
+ blocksForResource("a").withHashes("1", "2", "1");
+ List<CloneGroup> clones = OriginalCloneDetectionAlgorithm.detect(cloneIndex, fileBlocks);
+ print(clones);
+
+ assertThat(clones.size(), is(2));
+ Iterator<CloneGroup> clonesIterator = clones.iterator();
+
+ CloneGroup clone = clonesIterator.next();
+ assertThat(clone.getCloneUnitLength(), is(2));
+ assertThat(clone.getOriginPart(), is(newClonePart("a", 0, 2)));
+ assertThat(clone.getCloneParts(), hasItem(newClonePart("a", 0, 2)));
+ assertThat(clone.getCloneParts(), hasItem(newClonePart("b", 0, 2)));
+ assertThat(clone.getCloneParts(), hasItem(newClonePart("b", 2, 2)));
+
+ clone = clonesIterator.next();
+ assertThat(clone.getCloneUnitLength(), is(3));
+ assertThat(clone.getOriginPart(), is(newClonePart("a", 0, 3)));
+ assertThat(clone.getCloneParts(), hasItem(newClonePart("a", 0, 3)));
+ assertThat(clone.getCloneParts(), hasItem(newClonePart("b", 0, 3)));
+ }
+
+ /**
+ * Given:
+ * <pre>
+ * b: 1 2 1 2 1 2 1
+ * a: 1 2 1 2 1 2
+ * </pre>
+ * Expected:
+ * <pre>
+ * a-b-b (1 2 1 2 1) - note that there is overlapping among parts for "b"
+ * a-b (1 2 1 2 1 2)
+ * </pre>
+ */
+ @Test
+ public void problemWithNestedCloneGroups() {
+ CloneIndex cloneIndex = createIndex(
+ blocksForResource("b").withHashes("1", "2", "1", "2", "1", "2", "1"));
+ List<Block> fileBlocks =
+ blocksForResource("a").withHashes("1", "2", "1", "2", "1", "2");
+ List<CloneGroup> clones = OriginalCloneDetectionAlgorithm.detect(cloneIndex, fileBlocks);
+ print(clones);
+
+ assertThat(clones.size(), is(2));
+ Iterator<CloneGroup> clonesIterator = clones.iterator();
+
+ CloneGroup clone = clonesIterator.next();
+ assertThat(clone.getCloneUnitLength(), is(5));
+ assertThat(clone.getCloneParts().size(), is(3));
+ assertThat(clone.getOriginPart(), is(newClonePart("a", 0, 5)));
+ assertThat(clone.getCloneParts(), hasItem(newClonePart("a", 0, 5)));
+ assertThat(clone.getCloneParts(), hasItem(newClonePart("b", 0, 5)));
+ assertThat(clone.getCloneParts(), hasItem(newClonePart("b", 2, 5)));
+
+ clone = clonesIterator.next();
+ assertThat(clone.getCloneUnitLength(), is(6));
+ assertThat(clone.getCloneParts().size(), is(2));
+ assertThat(clone.getOriginPart(), is(newClonePart("a", 0, 6)));
+ assertThat(clone.getCloneParts(), hasItem(newClonePart("a", 0, 6)));
+ assertThat(clone.getCloneParts(), hasItem(newClonePart("b", 0, 6)));
+ }
+
+ /**
+ * Given:
+ * <pre>
+ * a: 1 2 3
+ * b: 1 2 4
+ * a: 1 2 5
+ * </pre>
+ * Expected:
+ * <pre>
+ * a-b (1 2) - instead of "a-a-b", which will be the case if file from index not ignored
+ * </pre>
+ */
+ @Test
+ public void fileAlreadyInIndex() {
+ CloneIndex cloneIndex = createIndex(
+ blocksForResource("a").withHashes("1", "2", "3"),
+ blocksForResource("b").withHashes("1", "2", "4"));
+ // Note about blocks with hashes "3", "4" and "5": those blocks here in order to not face another problem - with EOF (see separate test)
+ List<Block> fileBlocks =
+ blocksForResource("a").withHashes("1", "2", "5");
+ List<CloneGroup> clones = OriginalCloneDetectionAlgorithm.detect(cloneIndex, fileBlocks);
+ print(clones);
+
+ assertThat(clones.size(), is(1));
+ Iterator<CloneGroup> clonesIterator = clones.iterator();
+
+ CloneGroup clone = clonesIterator.next();
+ assertThat(clone.getCloneUnitLength(), is(2));
+ assertThat(clone.getCloneParts().size(), is(2));
+ assertThat(clone.getOriginPart(), is(newClonePart("a", 0, 2)));
+ assertThat(clone.getCloneParts(), hasItem(newClonePart("a", 0, 2)));
+ assertThat(clone.getCloneParts(), hasItem(newClonePart("b", 0, 2)));
+ }
+
+ /**
+ * Given: file with repeated hashes
+ * Expected: only one query of index for each unique hash
+ */
+ @Test
+ public void only_one_query_of_index_for_each_unique_hash() {
+ CloneIndex index = spy(createIndex());
+ List<Block> fileBlocks =
+ blocksForResource("a").withHashes("1", "2", "1", "2");
+ OriginalCloneDetectionAlgorithm.detect(index, fileBlocks);
+
+ verify(index).getBySequenceHash(new ByteArray("1".getBytes()));
+ verify(index).getBySequenceHash(new ByteArray("2".getBytes()));
+ verifyNoMoreInteractions(index);
+ }
+
+ /**
+ * Given file with two lines, containing following statements:
+ * <pre>
+ * 0: A,B,A,B
+ * 1: A,B,A
+ * </pre>
+ * with block size 5 each block will span both lines, and hashes will be:
+ * <pre>
+ * A,B,A,B,A=1
+ * B,A,B,A,B=2
+ * A,B,A,B,A=1
+ * </pre>
+ * Expected: one clone with two parts, which contain exactly the same lines
+ */
+ @Test
+ public void same_lines_but_different_indexes() {
+ CloneIndex cloneIndex = createIndex();
+ List<Block> fileBlocks = Arrays.asList(
+ new Block("a", new ByteArray("1".getBytes()), 0, 0, 1),
+ new Block("a", new ByteArray("2".getBytes()), 1, 0, 1),
+ new Block("a", new ByteArray("1".getBytes()), 2, 0, 1));
+ List<CloneGroup> clones = OriginalCloneDetectionAlgorithm.detect(cloneIndex, fileBlocks);
+ print(clones);
+
+ assertThat(clones.size(), is(1));
+ Iterator<CloneGroup> clonesIterator = clones.iterator();
+
+ CloneGroup clone = clonesIterator.next();
+ assertThat(clone.getCloneUnitLength(), is(1));
+ assertThat(clone.getCloneParts().size(), is(2));
+ assertThat(clone.getOriginPart(), is(new ClonePart("a", 0, 0, 1)));
+ assertThat(clone.getCloneParts(), hasItem(new ClonePart("a", 0, 0, 1)));
+ assertThat(clone.getCloneParts(), hasItem(new ClonePart("a", 2, 0, 1)));
+ }
+
+ /**
+ * Given: empty list of blocks for file
+ * Expected: {@link Collections#EMPTY_LIST}
+ */
+ @Test
+ public void shouldReturnEmptyListWhenNoBlocksForFile() {
+ List<CloneGroup> result = OriginalCloneDetectionAlgorithm.detect(null, new ArrayList<Block>());
+ assertThat(result, sameInstance(Collections.EMPTY_LIST));
+ }
+
+ private void print(List<CloneGroup> clones) {
+ for (CloneGroup clone : clones) {
+ System.out.println(clone);
+ }
+ System.out.println();
+ }
+
+ private static CloneIndex createIndex(List<Block>... blocks) {
+ CloneIndex cloneIndex = new MemoryCloneIndex();
+ for (List<Block> b : blocks) {
+ for (Block block : b) {
+ cloneIndex.insert(block);
+ }
+ }
+ return cloneIndex;
+ }
+
+ private static BlocksBuilder blocksForResource(String resourceId) {
+ return new BlocksBuilder(resourceId);
+ }
+
+ private static class BlocksBuilder {
+ String resourceId;
+
+ public BlocksBuilder(String resourceId) {
+ this.resourceId = resourceId;
+ }
+
+ List<Block> withHashes(String... hashes) {
+ ByteArray[] arrays = new ByteArray[hashes.length];
+ for (int i = 0; i < hashes.length; i++) {
+ arrays[i] = new ByteArray(hashes[i].getBytes());
+ }
+ return withHashes(arrays);
+ }
+
+ List<Block> withHashes(ByteArray... hashes) {
+ List<Block> result = Lists.newArrayList();
+ int index = 0;
+ for (ByteArray hash : hashes) {
+ result.add(newBlock(resourceId, hash, index));
+ index++;
+ }
+ return result;
+ }
+ }
+
+}
diff --git a/sonar-duplications/src/test/java/org/sonar/duplications/index/DataUtilsTest.java b/sonar-duplications/src/test/java/org/sonar/duplications/index/DataUtilsTest.java
new file mode 100644
index 00000000000..c210a4c8e05
--- /dev/null
+++ b/sonar-duplications/src/test/java/org/sonar/duplications/index/DataUtilsTest.java
@@ -0,0 +1,90 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2011 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar 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.
+ *
+ * Sonar 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 Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.duplications.index;
+
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+
+import java.util.Arrays;
+
+import org.junit.Test;
+
+public class DataUtilsTest {
+
+ @Test
+ public void testSort() {
+ int[] expected = new int[200];
+ int[] actual = new int[expected.length];
+ for (int i = 0; i < expected.length; i++) {
+ expected[i] = (int) (Math.random() * 900);
+ actual[i] = expected[i];
+ }
+ Arrays.sort(expected);
+ DataUtils.sort(new SimpleSortable(actual, actual.length));
+ assertThat(actual, equalTo(expected));
+ }
+
+ @Test
+ public void testSearch() {
+ int[] a = new int[] { 1, 2, 4, 4, 4, 5, 0 };
+ SimpleSortable sortable = new SimpleSortable(a, a.length - 1);
+ // search 4
+ a[a.length - 1] = 4;
+ assertThat(DataUtils.binarySearch(sortable), is(2));
+ // search 5
+ a[a.length - 1] = 5;
+ assertThat(DataUtils.binarySearch(sortable), is(5));
+ // search -5
+ a[a.length - 1] = -5;
+ assertThat(DataUtils.binarySearch(sortable), is(0));
+ // search 10
+ a[a.length - 1] = 10;
+ assertThat(DataUtils.binarySearch(sortable), is(6));
+ // search 3
+ a[a.length - 1] = 3;
+ assertThat(DataUtils.binarySearch(sortable), is(2));
+ }
+
+ class SimpleSortable implements DataUtils.Sortable {
+ private final int[] a;
+ private final int size;
+
+ public SimpleSortable(int[] a, int size) {
+ this.a = a;
+ this.size = size;
+ }
+
+ public int size() {
+ return size;
+ }
+
+ public void swap(int i, int j) {
+ int tmp = a[i];
+ a[i] = a[j];
+ a[j] = tmp;
+ }
+
+ public boolean isLess(int i, int j) {
+ return a[i] < a[j];
+ }
+ }
+
+}
diff --git a/sonar-duplications/src/test/java/org/sonar/duplications/index/MemoryCloneIndexTest.java b/sonar-duplications/src/test/java/org/sonar/duplications/index/MemoryCloneIndexTest.java
new file mode 100644
index 00000000000..fc44357d3aa
--- /dev/null
+++ b/sonar-duplications/src/test/java/org/sonar/duplications/index/MemoryCloneIndexTest.java
@@ -0,0 +1,102 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2011 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar 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.
+ *
+ * Sonar 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 Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.duplications.index;
+
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Collection;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.sonar.duplications.block.Block;
+import org.sonar.duplications.block.ByteArray;
+
+public class MemoryCloneIndexTest {
+
+ private CloneIndex cloneIndex;
+
+ @Before
+ public void initialize() {
+ cloneIndex = new MemoryCloneIndex();
+ }
+
+ @Test
+ public void byFileName() {
+ Block tuple1 = new Block("a", new ByteArray(0), 0, 0, 10);
+ Block tuple2 = new Block("a", new ByteArray(0), 1, 10, 20);
+
+ assertThat(cloneIndex.getByResourceId("a").size(), is(0));
+
+ cloneIndex.insert(tuple1);
+ assertThat(cloneIndex.getByResourceId("a").size(), is(1));
+
+ cloneIndex.insert(tuple2);
+ assertThat(cloneIndex.getByResourceId("a").size(), is(2));
+ }
+
+ @Test
+ public void bySequenceHash() {
+ Block tuple1 = new Block("a", new ByteArray(0), 0, 0, 5);
+ Block tuple2 = new Block("a", new ByteArray(0), 1, 1, 6);
+
+ assertThat(cloneIndex.getBySequenceHash(new ByteArray(0)).size(), is(0));
+
+ cloneIndex.insert(tuple1);
+ assertThat(cloneIndex.getBySequenceHash(new ByteArray(0)).size(), is(1));
+
+ cloneIndex.insert(tuple2);
+ assertThat(cloneIndex.getBySequenceHash(new ByteArray(0)).size(), is(2));
+ }
+
+ @Test
+ public void insertSame() {
+ Block tuple = new Block("a", new ByteArray(0), 0, 0, 5);
+ Block tupleSame = new Block("a", new ByteArray(0), 0, 0, 5);
+
+ assertThat(cloneIndex.getByResourceId("a").size(), is(0));
+ assertThat(cloneIndex.getBySequenceHash(new ByteArray(0)).size(), is(0));
+
+ cloneIndex.insert(tuple);
+ assertThat(cloneIndex.getByResourceId("a").size(), is(1));
+ assertThat(cloneIndex.getBySequenceHash(new ByteArray(0)).size(), is(1));
+
+ cloneIndex.insert(tupleSame);
+ assertThat(cloneIndex.getByResourceId("a").size(), is(1));
+ assertThat(cloneIndex.getBySequenceHash(new ByteArray(0)).size(), is(1));
+ }
+
+ @Test
+ public void testSorted() {
+ for (int i = 0; i < 10; i++) {
+ cloneIndex.insert(new Block("a", new ByteArray(1), 10 - i, i, i + 5));
+ }
+ assertThat(cloneIndex.getByResourceId("a").size(), is(10));
+ assertThat(cloneIndex.getBySequenceHash(new ByteArray(1)).size(), is(10));
+
+ Collection<Block> set = cloneIndex.getByResourceId("a");
+ int prevStatementIndex = 0;
+ for (Block tuple : set) {
+ assertTrue(tuple.getIndexInFile() > prevStatementIndex);
+ prevStatementIndex = tuple.getIndexInFile();
+ }
+ }
+}
diff --git a/sonar-duplications/src/test/java/org/sonar/duplications/index/PackedMemoryCloneIndexTest.java b/sonar-duplications/src/test/java/org/sonar/duplications/index/PackedMemoryCloneIndexTest.java
new file mode 100644
index 00000000000..4fc2c2b11a4
--- /dev/null
+++ b/sonar-duplications/src/test/java/org/sonar/duplications/index/PackedMemoryCloneIndexTest.java
@@ -0,0 +1,116 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2011 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar 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.
+ *
+ * Sonar 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 Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.duplications.index;
+
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.sameInstance;
+import static org.junit.Assert.assertThat;
+
+import java.util.Collection;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.sonar.duplications.block.Block;
+import org.sonar.duplications.block.ByteArray;
+
+public class PackedMemoryCloneIndexTest {
+
+ private PackedMemoryCloneIndex index;
+
+ @Before
+ public void setUp() {
+ index = new PackedMemoryCloneIndex();
+ }
+
+ @Test
+ public void test() {
+ index.insert(newBlock("a", 1));
+ index.insert(newBlock("a", 2));
+ index.insert(newBlock("b", 1));
+ index.insert(newBlock("c", 1));
+ index.insert(newBlock("d", 1));
+ index.insert(newBlock("e", 1));
+ index.insert(newBlock("e", 2));
+ index.insert(newBlock("e", 3));
+
+ assertThat(index.getBySequenceHash(new ByteArray(1L)).size(), is(5));
+ assertThat(index.getBySequenceHash(new ByteArray(2L)).size(), is(2));
+ assertThat(index.getBySequenceHash(new ByteArray(3L)).size(), is(1));
+ assertThat(index.getBySequenceHash(new ByteArray(4L)).size(), is(0));
+ assertThat(index.getByResourceId("a").size(), is(2));
+ assertThat(index.getByResourceId("b").size(), is(1));
+ assertThat(index.getByResourceId("e").size(), is(3));
+ assertThat(index.getByResourceId("does not exist").size(), is(0));
+ }
+
+ /**
+ * When: query by a hash value.
+ * Expected: all blocks should have same hash, which presented in the form of the same object.
+ */
+ @Test
+ public void should_construct_blocks_with_normalized_hash() {
+ index.insert(newBlock("a", 1));
+ index.insert(newBlock("b", 1));
+ index.insert(newBlock("c", 1));
+ ByteArray requestedHash = new ByteArray(1L);
+ Collection<Block> blocks = index.getBySequenceHash(requestedHash);
+ assertThat(blocks.size(), is(3));
+ for (Block block : blocks) {
+ assertThat(block.getBlockHash(), sameInstance(requestedHash));
+ }
+ }
+
+ /**
+ * Given: index with initial capacity 1.
+ * Expected: size and capacity should be increased after insertion of two blocks.
+ */
+ @Test
+ public void should_increase_capacity() {
+ CloneIndex index = new PackedMemoryCloneIndex(8, 1);
+ index.insert(newBlock("a", 1));
+ index.insert(newBlock("a", 2));
+ assertThat(index.getByResourceId("a").size(), is(2));
+ }
+
+ /**
+ * Given: index, which accepts blocks with 4-byte hash.
+ * Expected: exception during insertion of block with 8-byte hash.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void attempt_to_insert_hash_of_incorrect_size() {
+ CloneIndex index = new PackedMemoryCloneIndex(4, 1);
+ index.insert(newBlock("a", 1));
+ }
+
+ /**
+ * Given: index, which accepts blocks with 4-byte hash.
+ * Expected: exception during search by 8-byte hash.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void attempt_to_find_hash_of_incorrect_size() {
+ CloneIndex index = new PackedMemoryCloneIndex(4, 1);
+ index.getBySequenceHash(new ByteArray(1L));
+ }
+
+ private static Block newBlock(String resourceId, long hash) {
+ return new Block(resourceId, new ByteArray(hash), 1, 1, 1);
+ }
+
+}
diff --git a/sonar-duplications/src/test/java/org/sonar/duplications/java/JavaStatementBuilderTest.java b/sonar-duplications/src/test/java/org/sonar/duplications/java/JavaStatementBuilderTest.java
new file mode 100644
index 00000000000..2267fa8a125
--- /dev/null
+++ b/sonar-duplications/src/test/java/org/sonar/duplications/java/JavaStatementBuilderTest.java
@@ -0,0 +1,160 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2011 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar 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.
+ *
+ * Sonar 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 Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.duplications.java;
+
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.number.OrderingComparisons.greaterThan;
+import static org.junit.Assert.assertThat;
+
+import java.io.File;
+import java.util.List;
+
+import org.junit.Test;
+import org.sonar.duplications.DuplicationsTestUtil;
+import org.sonar.duplications.statement.Statement;
+import org.sonar.duplications.statement.StatementChunker;
+import org.sonar.duplications.token.TokenChunker;
+
+public class JavaStatementBuilderTest {
+
+ private TokenChunker tokenChunker = JavaTokenProducer.build();
+ private StatementChunker statementChunker = JavaStatementBuilder.build();
+
+ private List<Statement> chunk(String sourceCode) {
+ return statementChunker.chunk(tokenChunker.chunk(sourceCode));
+ }
+
+ @Test
+ public void shouldIgnoreImportStatement() {
+ assertThat(chunk("import org.sonar.duplications.java;").size(), is(0));
+ }
+
+ @Test
+ public void shouldIgnorePackageStatement() {
+ assertThat(chunk("package org.sonar.duplications.java;").size(), is(0));
+ }
+
+ @Test
+ public void shouldHandleAnnotation() {
+ List<Statement> statements = chunk("" +
+ "@Entity" +
+ "@Table(name = \"properties\")" +
+ "@Column(updatable = true, nullable = true)");
+ assertThat(statements.size(), is(3));
+ assertThat(statements.get(0).getValue(), is("@Entity"));
+ assertThat(statements.get(1).getValue(), is("@Table(name=$CHARS)"));
+ assertThat(statements.get(2).getValue(), is("@Column(updatable=true,nullable=true)"));
+ }
+
+ @Test
+ public void shouldHandleIf() {
+ List<Statement> statements = chunk("if (a > b) { something(); }");
+ assertThat(statements.size(), is(2));
+ assertThat(statements.get(0).getValue(), is("if(a>b)"));
+ assertThat(statements.get(1).getValue(), is("something()"));
+
+ statements = chunk("if (a > b) { something(); } else { somethingOther(); }");
+ assertThat(statements.size(), is(4));
+ assertThat(statements.get(0).getValue(), is("if(a>b)"));
+ assertThat(statements.get(1).getValue(), is("something()"));
+ assertThat(statements.get(2).getValue(), is("else"));
+ assertThat(statements.get(3).getValue(), is("somethingOther()"));
+
+ statements = chunk("if (a > 0) { something(); } else if (a == 0) { somethingOther(); }");
+ assertThat(statements.size(), is(4));
+ assertThat(statements.get(0).getValue(), is("if(a>$NUMBER)"));
+ assertThat(statements.get(1).getValue(), is("something()"));
+ assertThat(statements.get(2).getValue(), is("elseif(a==$NUMBER)"));
+ assertThat(statements.get(3).getValue(), is("somethingOther()"));
+ }
+
+ @Test
+ public void shouldHandleFor() {
+ List<Statement> statements = chunk("for (int i = 0; i < 10; i++) { something(); }");
+ assertThat(statements.size(), is(2));
+ assertThat(statements.get(0).getValue(), is("for(inti=$NUMBER;i<$NUMBER;i++)"));
+ assertThat(statements.get(1).getValue(), is("something()"));
+
+ statements = chunk("for (Item item : items) { something(); }");
+ assertThat(statements.size(), is(2));
+ assertThat(statements.get(0).getValue(), is("for(Itemitem:items)"));
+ assertThat(statements.get(1).getValue(), is("something()"));
+ }
+
+ @Test
+ public void shouldHandleWhile() {
+ List<Statement> statements = chunk("while (i < args.length) { something(); }");
+ assertThat(statements.size(), is(2));
+ assertThat(statements.get(0).getValue(), is("while(i<args.length)"));
+ assertThat(statements.get(1).getValue(), is("something()"));
+
+ statements = chunk("while (true);");
+ assertThat(statements.size(), is(1));
+ assertThat(statements.get(0).getValue(), is("while(true)"));
+ }
+
+ @Test
+ public void shouldHandleDoWhile() {
+ List<Statement> statements = chunk("do { something(); } while (true);");
+ assertThat(statements.size(), is(3));
+ assertThat(statements.get(0).getValue(), is("do"));
+ assertThat(statements.get(1).getValue(), is("something()"));
+ assertThat(statements.get(2).getValue(), is("while(true)"));
+ }
+
+ @Test
+ public void shouldHandleSwitch() {
+ List<Statement> statements = chunk("" +
+ "switch (month) {" +
+ " case 1 : monthString=\"January\"; break;" +
+ " case 2 : monthString=\"February\"; break;" +
+ " default: monthString=\"Invalid\";");
+ assertThat(statements.size(), is(9));
+ assertThat(statements.get(0).getValue(), is("switch(month)"));
+ assertThat(statements.get(1).getValue(), is("case$NUMBER:"));
+ assertThat(statements.get(2).getValue(), is("monthString=$CHARS"));
+ assertThat(statements.get(3).getValue(), is("break"));
+ assertThat(statements.get(4).getValue(), is("case$NUMBER:"));
+ assertThat(statements.get(5).getValue(), is("monthString=$CHARS"));
+ assertThat(statements.get(6).getValue(), is("break"));
+ assertThat(statements.get(7).getValue(), is("default:"));
+ assertThat(statements.get(8).getValue(), is("monthString=$CHARS"));
+ }
+
+ @Test
+ public void shouldHandleArray() {
+ List<Statement> statements = chunk("new Integer[][] { { 1, 2 }, {3, 4} };");
+ assertThat(statements.size(), is(4));
+ assertThat(statements.get(0).getValue(), is("newInteger[][]"));
+ assertThat(statements.get(1).getValue(), is("$NUMBER,$NUMBER"));
+ assertThat(statements.get(2).getValue(), is(","));
+ assertThat(statements.get(3).getValue(), is("$NUMBER,$NUMBER"));
+ }
+
+ @Test
+ public void realExamples() {
+ File testFile = DuplicationsTestUtil.findFile("/java/MessageResources.java");
+ assertThat(statementChunker.chunk(tokenChunker.chunk(testFile)).size(), greaterThan(0));
+
+ testFile = DuplicationsTestUtil.findFile("/java/RequestUtils.java");
+ assertThat(statementChunker.chunk(tokenChunker.chunk(testFile)).size(), greaterThan(0));
+ }
+
+}
diff --git a/sonar-duplications/src/test/java/org/sonar/duplications/java/JavaTokenProducerTest.java b/sonar-duplications/src/test/java/org/sonar/duplications/java/JavaTokenProducerTest.java
new file mode 100644
index 00000000000..285cdf2c61e
--- /dev/null
+++ b/sonar-duplications/src/test/java/org/sonar/duplications/java/JavaTokenProducerTest.java
@@ -0,0 +1,293 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2011 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar 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.
+ *
+ * Sonar 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 Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.duplications.java;
+
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.number.OrderingComparisons.greaterThan;
+import static org.junit.Assert.assertThat;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.List;
+
+import org.hamcrest.Matcher;
+import org.junit.Test;
+import org.sonar.duplications.DuplicationsTestUtil;
+import org.sonar.duplications.token.Token;
+import org.sonar.duplications.token.TokenChunker;
+
+import com.google.common.collect.Lists;
+
+/**
+ * See <a href="http://java.sun.com/docs/books/jls/third_edition/html/lexical.html">The Java Language Specification, Third Edition: Lexical Structure</a>
+ *
+ * TODO Java 7 features: Binary Integer Literals, Using Underscore Characters in Numeric Literals
+ * TODO add more complex example
+ */
+public class JavaTokenProducerTest {
+
+ private TokenChunker chunker = JavaTokenProducer.build();
+
+ /**
+ * <a href="http://java.sun.com/docs/books/jls/third_edition/html/lexical.html#3.6">White Space</a>
+ */
+ @Test
+ public void shouldIgnoreWhitespaces() {
+ assertThat(chunk(" \t\f\n\r"), isTokens());
+ }
+
+ /**
+ * <a href="http://java.sun.com/docs/books/jls/third_edition/html/lexical.html#3.7">Comments</a>
+ */
+ @Test
+ public void shouldIgnoreEndOfLineComment() {
+ assertThat(chunk("// This is a comment"), isTokens());
+ assertThat(chunk("// This is a comment \n and_this_is_not"), isTokens(new Token("and_this_is_not", 2, 1)));
+ }
+
+ @Test
+ public void shouldIgnoreTraditionalComment() {
+ assertThat(chunk("/* This is a comment \n and the second line */"), isTokens());
+ assertThat(chunk("/** This is a javadoc \n and the second line */"), isTokens());
+ assertThat(chunk("/* this \n comment /* \n // /** ends \n here: */"), isTokens());
+ }
+
+ /**
+ * <a href="http://java.sun.com/docs/books/jls/third_edition/html/lexical.html#3.8">Identifiers</a>
+ */
+ @Test
+ public void shouldPreserveIdentifiers() {
+ assertThat(chunk("String"), isTokens(new Token("String", 1, 0)));
+ assertThat(chunk("i3"), isTokens(new Token("i3", 1, 0)));
+ assertThat(chunk("MAX_VALUE"), isTokens(new Token("MAX_VALUE", 1, 0)));
+ assertThat(chunk("isLetterOrDigit"), isTokens(new Token("isLetterOrDigit", 1, 0)));
+
+ assertThat(chunk("_"), isTokens(new Token("_", 1, 0)));
+ assertThat(chunk("_123_"), isTokens(new Token("_123_", 1, 0)));
+ assertThat(chunk("_Field"), isTokens(new Token("_Field", 1, 0)));
+ assertThat(chunk("_Field5"), isTokens(new Token("_Field5", 1, 0)));
+
+ assertThat(chunk("$"), isTokens(new Token("$", 1, 0)));
+ assertThat(chunk("$field"), isTokens(new Token("$field", 1, 0)));
+
+ assertThat(chunk("i2j"), isTokens(new Token("i2j", 1, 0)));
+ assertThat(chunk("from1to4"), isTokens(new Token("from1to4", 1, 0)));
+
+ assertThat("identifier with unicode", chunk("αβγ"), isTokens(new Token("αβγ", 1, 0)));
+ }
+
+ /**
+ * <a href="http://java.sun.com/docs/books/jls/third_edition/html/lexical.html#3.9">Keywords</a>
+ */
+ @Test
+ public void shouldPreserverKeywords() {
+ assertThat(chunk("private static final"), isTokens(new Token("private", 1, 0), new Token("static", 1, 8), new Token("final", 1, 15)));
+ }
+
+ /**
+ * <a href="http://java.sun.com/docs/books/jls/third_edition/html/lexical.html#3.10.1">Integer Literals</a>
+ */
+ @Test
+ public void shouldNormalizeDecimalIntegerLiteral() {
+ assertThat(chunk("543"), isNumericLiteral());
+ assertThat(chunk("543l"), isNumericLiteral());
+ assertThat(chunk("543L"), isNumericLiteral());
+ }
+
+ @Test
+ public void shouldNormalizeOctalIntegerLiteral() {
+ assertThat(chunk("077"), isNumericLiteral());
+ assertThat(chunk("077l"), isNumericLiteral());
+ assertThat(chunk("077L"), isNumericLiteral());
+ }
+
+ @Test
+ public void shouldNormalizeHexIntegerLiteral() {
+ assertThat(chunk("0xFF"), isNumericLiteral());
+ assertThat(chunk("0xFFl"), isNumericLiteral());
+ assertThat(chunk("0xFFL"), isNumericLiteral());
+ }
+
+ /**
+ * <a href="http://java.sun.com/docs/books/jls/third_edition/html/lexical.html#3.10.2">Floating-Point Literals</a>
+ */
+ @Test
+ public void shouldNormalizeDecimalFloatingPointLiteral() {
+ // with dot at the end
+ assertThat(chunk("1234."), isNumericLiteral());
+ assertThat(chunk("1234.E1"), isNumericLiteral());
+ assertThat(chunk("1234.e+1"), isNumericLiteral());
+ assertThat(chunk("1234.E-1"), isNumericLiteral());
+ assertThat(chunk("1234.f"), isNumericLiteral());
+
+ // with dot between
+ assertThat(chunk("12.34"), isNumericLiteral());
+ assertThat(chunk("12.34E1"), isNumericLiteral());
+ assertThat(chunk("12.34e+1"), isNumericLiteral());
+ assertThat(chunk("12.34E-1"), isNumericLiteral());
+
+ assertThat(chunk("12.34f"), isNumericLiteral());
+ assertThat(chunk("12.34E1F"), isNumericLiteral());
+ assertThat(chunk("12.34E+1d"), isNumericLiteral());
+ assertThat(chunk("12.34e-1D"), isNumericLiteral());
+
+ // with dot at the beginning
+ assertThat(chunk(".1234"), isNumericLiteral());
+ assertThat(chunk(".1234e1"), isNumericLiteral());
+ assertThat(chunk(".1234E+1"), isNumericLiteral());
+ assertThat(chunk(".1234E-1"), isNumericLiteral());
+
+ assertThat(chunk(".1234f"), isNumericLiteral());
+ assertThat(chunk(".1234E1F"), isNumericLiteral());
+ assertThat(chunk(".1234e+1d"), isNumericLiteral());
+ assertThat(chunk(".1234E-1D"), isNumericLiteral());
+
+ // without dot
+ assertThat(chunk("1234e1"), isNumericLiteral());
+ assertThat(chunk("1234E+1"), isNumericLiteral());
+ assertThat(chunk("1234E-1"), isNumericLiteral());
+
+ assertThat(chunk("1234E1f"), isNumericLiteral());
+ assertThat(chunk("1234e+1d"), isNumericLiteral());
+ assertThat(chunk("1234E-1D"), isNumericLiteral());
+ }
+
+ @Test
+ public void shouldNormalizeHexadecimalFloatingPointLiteral() {
+ // with dot at the end
+ assertThat(chunk("0xAF."), isNumericLiteral());
+ assertThat(chunk("0XAF.P1"), isNumericLiteral());
+ assertThat(chunk("0xAF.p+1"), isNumericLiteral());
+ assertThat(chunk("0XAF.p-1"), isNumericLiteral());
+ assertThat(chunk("0xAF.f"), isNumericLiteral());
+
+ // with dot between
+ assertThat(chunk("0XAF.BC"), isNumericLiteral());
+ assertThat(chunk("0xAF.BCP1"), isNumericLiteral());
+ assertThat(chunk("0XAF.BCp+1"), isNumericLiteral());
+ assertThat(chunk("0xAF.BCP-1"), isNumericLiteral());
+
+ assertThat(chunk("0xAF.BCf"), isNumericLiteral());
+ assertThat(chunk("0xAF.BCp1F"), isNumericLiteral());
+ assertThat(chunk("0XAF.BCP+1d"), isNumericLiteral());
+ assertThat(chunk("0XAF.BCp-1D"), isNumericLiteral());
+
+ // without dot
+ assertThat(chunk("0xAFp1"), isNumericLiteral());
+ assertThat(chunk("0XAFp+1"), isNumericLiteral());
+ assertThat(chunk("0xAFp-1"), isNumericLiteral());
+
+ assertThat(chunk("0XAFp1f"), isNumericLiteral());
+ assertThat(chunk("0xAFp+1d"), isNumericLiteral());
+ assertThat(chunk("0XAFp-1D"), isNumericLiteral());
+ }
+
+ /**
+ * <a href="http://java.sun.com/docs/books/jls/third_edition/html/lexical.html#3.10.3">Boolean Literals</a>
+ */
+ @Test
+ public void shouldPreserveBooleanLiterals() {
+ assertThat(chunk("true false"), isTokens(new Token("true", 1, 0), new Token("false", 1, 5)));
+ }
+
+ /**
+ * <a href="http://java.sun.com/docs/books/jls/third_edition/html/lexical.html#3.10.4">Character Literals</a>
+ */
+ @Test
+ public void shouldNormalizeCharacterLiterals() {
+ assertThat("single character", chunk("'a'"), isStringLiteral());
+ assertThat("escaped LF", chunk("'\\n'"), isStringLiteral());
+ assertThat("escaped quote", chunk("'\\''"), isStringLiteral());
+ assertThat("octal escape", chunk("'\\177'"), isStringLiteral());
+ assertThat("unicode escape", chunk("'\\u03a9'"), isStringLiteral());
+ }
+
+ /**
+ * <a href="http://java.sun.com/docs/books/jls/third_edition/html/lexical.html#3.10.5">String Literals</a>
+ */
+ @Test
+ public void shouldNormalizeStringLiterals() {
+ assertThat("regular string", chunk("\"string\""), isStringLiteral());
+ assertThat("empty string", chunk("\"\""), isStringLiteral());
+ assertThat("escaped LF", chunk("\"\\n\""), isStringLiteral());
+ assertThat("escaped double quotes", chunk("\"string, which contains \\\"escaped double quotes\\\"\""), isStringLiteral());
+ assertThat("octal escape", chunk("\"string \\177\""), isStringLiteral());
+ assertThat("unicode escape", chunk("\"string \\u03a9\""), isStringLiteral());
+ }
+
+ /**
+ * <a href="http://java.sun.com/docs/books/jls/third_edition/html/lexical.html#3.10.7">The Null Literal</a>
+ */
+ @Test
+ public void shouldPreserverNullLiteral() {
+ assertThat(chunk("null"), isTokens(new Token("null", 1, 0)));
+ }
+
+ /**
+ * <a href="http://java.sun.com/docs/books/jls/third_edition/html/lexical.html#3.11">Separators</a>
+ */
+ @Test
+ public void shouldPreserveSeparators() {
+ assertThat(chunk("(){}[];,."), isTokens(
+ new Token("(", 1, 0), new Token(")", 1, 1),
+ new Token("{", 1, 2), new Token("}", 1, 3),
+ new Token("[", 1, 4), new Token("]", 1, 5),
+ new Token(";", 1, 6), new Token(",", 1, 7),
+ new Token(".", 1, 8)));
+ }
+
+ /**
+ * <a href="http://java.sun.com/docs/books/jls/third_edition/html/lexical.html#3.12">Operators</a>
+ */
+ @Test
+ public void shouldPreserveOperators() {
+ assertThat(chunk("+="), isTokens(new Token("+", 1, 0), new Token("=", 1, 1)));
+ assertThat(chunk("--"), isTokens(new Token("-", 1, 0), new Token("-", 1, 1)));
+ }
+
+ @Test
+ public void realExamples() {
+ File testFile = DuplicationsTestUtil.findFile("/java/MessageResources.java");
+ assertThat(chunker.chunk(testFile).size(), greaterThan(0));
+
+ testFile = DuplicationsTestUtil.findFile("/java/RequestUtils.java");
+ assertThat(chunker.chunk(testFile).size(), greaterThan(0));
+ }
+
+ private static Matcher<List<Token>> isNumericLiteral() {
+ return isTokens(new Token("$NUMBER", 1, 0));
+ }
+
+ private static Matcher<List<Token>> isStringLiteral() {
+ return isTokens(new Token("$CHARS", 1, 0));
+ }
+
+ /**
+ * @return matcher for list of tokens
+ */
+ private static Matcher<List<Token>> isTokens(Token... tokens) {
+ return is(Arrays.asList(tokens));
+ }
+
+ private List<Token> chunk(String sourceCode) {
+ return Lists.newArrayList(chunker.chunk(sourceCode));
+ }
+
+}
diff --git a/sonar-duplications/src/test/java/org/sonar/duplications/junit/TestNamePrinter.java b/sonar-duplications/src/test/java/org/sonar/duplications/junit/TestNamePrinter.java
new file mode 100644
index 00000000000..bc7097f4b3b
--- /dev/null
+++ b/sonar-duplications/src/test/java/org/sonar/duplications/junit/TestNamePrinter.java
@@ -0,0 +1,32 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2011 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar 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.
+ *
+ * Sonar 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 Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.duplications.junit;
+
+import org.junit.rules.TestWatchman;
+import org.junit.runners.model.FrameworkMethod;
+
+public class TestNamePrinter extends TestWatchman {
+
+ @Override
+ public void starting(FrameworkMethod method) {
+ System.out.println("Executing " + method.getName());
+ }
+
+}
diff --git a/sonar-duplications/src/test/java/org/sonar/duplications/statement/StatementChannelDisptacherTest.java b/sonar-duplications/src/test/java/org/sonar/duplications/statement/StatementChannelDisptacherTest.java
new file mode 100644
index 00000000000..c7d7e53f770
--- /dev/null
+++ b/sonar-duplications/src/test/java/org/sonar/duplications/statement/StatementChannelDisptacherTest.java
@@ -0,0 +1,70 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2011 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar 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.
+ *
+ * Sonar 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 Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.duplications.statement;
+
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyListOf;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.junit.Test;
+import org.sonar.duplications.statement.matcher.TokenMatcher;
+import org.sonar.duplications.token.Token;
+import org.sonar.duplications.token.TokenQueue;
+
+public class StatementChannelDisptacherTest {
+
+ @Test(expected = IllegalStateException.class)
+ public void shouldThrowAnException() {
+ TokenMatcher tokenMatcher = mock(TokenMatcher.class);
+ StatementChannel channel = StatementChannel.create(tokenMatcher);
+ StatementChannelDisptacher dispatcher = new StatementChannelDisptacher(Arrays.asList(channel));
+ TokenQueue tokenQueue = mock(TokenQueue.class);
+ when(tokenQueue.peek()).thenReturn(new Token("a", 1, 0)).thenReturn(null);
+ List<Statement> statements = mock(List.class);
+
+ dispatcher.consume(tokenQueue, statements);
+ }
+
+ @Test
+ public void shouldConsume() {
+ TokenMatcher tokenMatcher = mock(TokenMatcher.class);
+ when(tokenMatcher.matchToken(any(TokenQueue.class), anyListOf(Token.class))).thenReturn(true);
+ StatementChannel channel = StatementChannel.create(tokenMatcher);
+ StatementChannelDisptacher dispatcher = new StatementChannelDisptacher(Arrays.asList(channel));
+ TokenQueue tokenQueue = mock(TokenQueue.class);
+ when(tokenQueue.peek()).thenReturn(new Token("a", 1, 0)).thenReturn(null);
+ List<Statement> statements = mock(List.class);
+
+ assertThat(dispatcher.consume(tokenQueue, statements), is(true));
+ verify(tokenQueue, times(2)).peek();
+ verifyNoMoreInteractions(tokenQueue);
+ verifyNoMoreInteractions(statements);
+ }
+
+}
diff --git a/sonar-duplications/src/test/java/org/sonar/duplications/statement/StatementChannelTest.java b/sonar-duplications/src/test/java/org/sonar/duplications/statement/StatementChannelTest.java
new file mode 100644
index 00000000000..35ed1954fac
--- /dev/null
+++ b/sonar-duplications/src/test/java/org/sonar/duplications/statement/StatementChannelTest.java
@@ -0,0 +1,101 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2011 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar 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.
+ *
+ * Sonar 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 Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.duplications.statement;
+
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
+import org.sonar.duplications.statement.matcher.AnyTokenMatcher;
+import org.sonar.duplications.statement.matcher.TokenMatcher;
+import org.sonar.duplications.token.Token;
+import org.sonar.duplications.token.TokenQueue;
+
+public class StatementChannelTest {
+
+ @Test(expected = IllegalArgumentException.class)
+ public void shouldNotAcceptNull() {
+ StatementChannel.create((TokenMatcher[]) null);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void shouldNotAcceptEmpty() {
+ StatementChannel.create(new TokenMatcher[] {});
+ }
+
+ @Test
+ public void shouldPushForward() {
+ TokenQueue tokenQueue = mock(TokenQueue.class);
+ TokenMatcher matcher = mock(TokenMatcher.class);
+ List<Statement> output = mock(List.class);
+ StatementChannel channel = StatementChannel.create(matcher);
+
+ assertThat(channel.consume(tokenQueue, output), is(false));
+ ArgumentCaptor<List> matchedTokenList = ArgumentCaptor.forClass(List.class);
+ verify(matcher).matchToken(Mockito.eq(tokenQueue), matchedTokenList.capture());
+ verifyNoMoreInteractions(matcher);
+ verify(tokenQueue).pushForward(matchedTokenList.getValue());
+ verifyNoMoreInteractions(tokenQueue);
+ verifyNoMoreInteractions(output);
+ }
+
+ @Test
+ public void shouldCreateStatement() {
+ Token token = new Token("a", 1, 1);
+ TokenQueue tokenQueue = spy(new TokenQueue(Arrays.asList(token)));
+ TokenMatcher matcher = spy(new AnyTokenMatcher());
+ StatementChannel channel = StatementChannel.create(matcher);
+ List<Statement> output = mock(List.class);
+
+ assertThat(channel.consume(tokenQueue, output), is(true));
+ verify(matcher).matchToken(Mockito.eq(tokenQueue), Mockito.anyList());
+ verifyNoMoreInteractions(matcher);
+ ArgumentCaptor<Statement> statement = ArgumentCaptor.forClass(Statement.class);
+ verify(output).add(statement.capture());
+ assertThat(statement.getValue().getValue(), is("a"));
+ assertThat(statement.getValue().getStartLine(), is(1));
+ assertThat(statement.getValue().getEndLine(), is(1));
+ verifyNoMoreInteractions(output);
+ }
+
+ @Test
+ public void shouldNotCreateStatement() {
+ TokenQueue tokenQueue = spy(new TokenQueue(Arrays.asList(new Token("a", 1, 1))));
+ TokenMatcher matcher = spy(new AnyTokenMatcher());
+ StatementChannel channel = StatementChannel.create(matcher);
+ List<Statement> output = mock(List.class);
+
+ assertThat(channel.consume(tokenQueue, output), is(true));
+ verify(matcher).matchToken(Mockito.eq(tokenQueue), Mockito.anyList());
+ verifyNoMoreInteractions(matcher);
+ verify(output).add(Mockito.any(Statement.class));
+ verifyNoMoreInteractions(output);
+ }
+
+}
diff --git a/sonar-duplications/src/test/java/org/sonar/duplications/statement/StatementChunkerTest.java b/sonar-duplications/src/test/java/org/sonar/duplications/statement/StatementChunkerTest.java
new file mode 100644
index 00000000000..83b567abaff
--- /dev/null
+++ b/sonar-duplications/src/test/java/org/sonar/duplications/statement/StatementChunkerTest.java
@@ -0,0 +1,31 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2011 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar 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.
+ *
+ * Sonar 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 Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.duplications.statement;
+
+import org.junit.Test;
+
+public class StatementChunkerTest {
+
+ @Test(expected = IllegalArgumentException.class)
+ public void shouldNotAcceptNull() {
+ StatementChunker.builder().build().chunk(null);
+ }
+
+}
diff --git a/sonar-duplications/src/test/java/org/sonar/duplications/statement/StatementTest.java b/sonar-duplications/src/test/java/org/sonar/duplications/statement/StatementTest.java
new file mode 100644
index 00000000000..522b21f3496
--- /dev/null
+++ b/sonar-duplications/src/test/java/org/sonar/duplications/statement/StatementTest.java
@@ -0,0 +1,51 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2011 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar 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.
+ *
+ * Sonar 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 Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.duplications.statement;
+
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+import org.junit.Test;
+import org.sonar.duplications.token.Token;
+
+public class StatementTest {
+
+ @Test(expected = IllegalArgumentException.class)
+ public void shouldNotAcceptNull() {
+ new Statement(null);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void shouldNotAcceptEmpty() {
+ new Statement(new ArrayList<Token>());
+ }
+
+ @Test
+ public void shouldCreateStatementFromListOfTokens() {
+ Statement statement = new Statement(Arrays.asList(new Token("a", 1, 1), new Token("b", 2, 1)));
+ assertThat(statement.getValue(), is("ab"));
+ assertThat(statement.getStartLine(), is(1));
+ assertThat(statement.getEndLine(), is(2));
+ }
+
+}
diff --git a/sonar-duplications/src/test/java/org/sonar/duplications/statement/TokenMatcherFactoryTest.java b/sonar-duplications/src/test/java/org/sonar/duplications/statement/TokenMatcherFactoryTest.java
new file mode 100644
index 00000000000..20be1e85869
--- /dev/null
+++ b/sonar-duplications/src/test/java/org/sonar/duplications/statement/TokenMatcherFactoryTest.java
@@ -0,0 +1,48 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2011 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar 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.
+ *
+ * Sonar 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 Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.duplications.statement;
+
+import static org.hamcrest.Matchers.instanceOf;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.mock;
+
+import org.junit.Test;
+import org.sonar.duplications.statement.matcher.AnyTokenMatcher;
+import org.sonar.duplications.statement.matcher.BridgeTokenMatcher;
+import org.sonar.duplications.statement.matcher.ExactTokenMatcher;
+import org.sonar.duplications.statement.matcher.ForgetLastTokenMatcher;
+import org.sonar.duplications.statement.matcher.OptTokenMatcher;
+import org.sonar.duplications.statement.matcher.TokenMatcher;
+import org.sonar.duplications.statement.matcher.UptoTokenMatcher;
+
+public class TokenMatcherFactoryTest {
+
+ @Test
+ public void shouldCreateMatchers() {
+ assertThat(TokenMatcherFactory.anyToken(), instanceOf(AnyTokenMatcher.class));
+ assertThat(TokenMatcherFactory.bridge("(", ")"), instanceOf(BridgeTokenMatcher.class));
+ assertThat(TokenMatcherFactory.forgetLastToken(), instanceOf(ForgetLastTokenMatcher.class));
+ assertThat(TokenMatcherFactory.from("if"), instanceOf(ExactTokenMatcher.class));
+ assertThat(TokenMatcherFactory.opt(mock(TokenMatcher.class)), instanceOf(OptTokenMatcher.class));
+ assertThat(TokenMatcherFactory.to(";"), instanceOf(UptoTokenMatcher.class));
+ assertThat(TokenMatcherFactory.token(";"), instanceOf(ExactTokenMatcher.class));
+ }
+
+}
diff --git a/sonar-duplications/src/test/java/org/sonar/duplications/statement/matcher/AnyTokenMatcherTest.java b/sonar-duplications/src/test/java/org/sonar/duplications/statement/matcher/AnyTokenMatcherTest.java
new file mode 100644
index 00000000000..e607af5ce64
--- /dev/null
+++ b/sonar-duplications/src/test/java/org/sonar/duplications/statement/matcher/AnyTokenMatcherTest.java
@@ -0,0 +1,53 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2011 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar 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.
+ *
+ * Sonar 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 Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.duplications.statement.matcher;
+
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.junit.Test;
+import org.sonar.duplications.token.Token;
+import org.sonar.duplications.token.TokenQueue;
+
+public class AnyTokenMatcherTest {
+
+ @Test
+ public void shouldMatch() {
+ Token t1 = new Token("a", 1, 1);
+ Token t2 = new Token("b", 2, 1);
+ TokenQueue tokenQueue = spy(new TokenQueue(Arrays.asList(t1, t2)));
+ List<Token> output = mock(List.class);
+ AnyTokenMatcher matcher = new AnyTokenMatcher();
+
+ assertThat(matcher.matchToken(tokenQueue, output), is(true));
+ verify(tokenQueue).poll();
+ verifyNoMoreInteractions(tokenQueue);
+ verify(output).add(t1);
+ verifyNoMoreInteractions(output);
+ }
+
+}
diff --git a/sonar-duplications/src/test/java/org/sonar/duplications/statement/matcher/BridgeTokenMatcherTest.java b/sonar-duplications/src/test/java/org/sonar/duplications/statement/matcher/BridgeTokenMatcherTest.java
new file mode 100644
index 00000000000..2f73d4a55de
--- /dev/null
+++ b/sonar-duplications/src/test/java/org/sonar/duplications/statement/matcher/BridgeTokenMatcherTest.java
@@ -0,0 +1,106 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2011 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar 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.
+ *
+ * Sonar 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 Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.duplications.statement.matcher;
+
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.junit.Test;
+import org.sonar.duplications.token.Token;
+import org.sonar.duplications.token.TokenQueue;
+
+public class BridgeTokenMatcherTest {
+
+ @Test(expected = IllegalArgumentException.class)
+ public void shouldNotAcceptNullAsLeft() {
+ new BridgeTokenMatcher(null, ")");
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void shouldNotAcceptNullAsRight() {
+ new BridgeTokenMatcher("(", null);
+ }
+
+ @Test
+ public void shouldMatch() {
+ Token t1 = new Token("(", 1, 1);
+ Token t2 = new Token("a", 2, 1);
+ Token t3 = new Token("(", 3, 1);
+ Token t4 = new Token("b", 4, 1);
+ Token t5 = new Token(")", 5, 1);
+ Token t6 = new Token("c", 6, 1);
+ Token t7 = new Token(")", 7, 1);
+ TokenQueue tokenQueue = spy(new TokenQueue(Arrays.asList(t1, t2, t3, t4, t5, t6, t7)));
+ List<Token> output = mock(List.class);
+ BridgeTokenMatcher matcher = new BridgeTokenMatcher("(", ")");
+
+ assertThat(matcher.matchToken(tokenQueue, output), is(true));
+ verify(tokenQueue, times(1)).isNextTokenValue("(");
+ verify(tokenQueue, times(7)).poll();
+ verify(tokenQueue, times(7)).peek();
+ verifyNoMoreInteractions(tokenQueue);
+ verify(output).add(t1);
+ verify(output).add(t2);
+ verify(output).add(t3);
+ verify(output).add(t4);
+ verify(output).add(t5);
+ verify(output).add(t6);
+ verify(output).add(t7);
+ verifyNoMoreInteractions(output);
+ }
+
+ @Test
+ public void shouldNotMatchWhenNoLeft() {
+ Token t1 = new Token("a", 1, 1);
+ TokenQueue tokenQueue = spy(new TokenQueue(Arrays.asList(t1)));
+ List<Token> output = mock(List.class);
+ BridgeTokenMatcher matcher = new BridgeTokenMatcher("(", ")");
+
+ assertThat(matcher.matchToken(tokenQueue, output), is(false));
+ verify(tokenQueue).isNextTokenValue("(");
+ verifyNoMoreInteractions(tokenQueue);
+ verifyNoMoreInteractions(output);
+ }
+
+ @Test
+ public void shouldNotMatchWhenNoRight() {
+ Token t1 = new Token("(", 1, 1);
+ TokenQueue tokenQueue = spy(new TokenQueue(Arrays.asList(t1)));
+ List<Token> output = mock(List.class);
+ BridgeTokenMatcher matcher = new BridgeTokenMatcher("(", ")");
+
+ assertThat(matcher.matchToken(tokenQueue, output), is(false));
+ verify(tokenQueue, times(1)).isNextTokenValue("(");
+ verify(tokenQueue, times(1)).poll();
+ verify(tokenQueue, times(2)).peek();
+ verifyNoMoreInteractions(tokenQueue);
+ verify(output).add(t1);
+ verifyNoMoreInteractions(output);
+ }
+
+}
diff --git a/sonar-duplications/src/test/java/org/sonar/duplications/statement/matcher/ExactTokenMatcherTest.java b/sonar-duplications/src/test/java/org/sonar/duplications/statement/matcher/ExactTokenMatcherTest.java
new file mode 100644
index 00000000000..925d1d9ca63
--- /dev/null
+++ b/sonar-duplications/src/test/java/org/sonar/duplications/statement/matcher/ExactTokenMatcherTest.java
@@ -0,0 +1,73 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2011 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar 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.
+ *
+ * Sonar 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 Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.duplications.statement.matcher;
+
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.junit.Test;
+import org.sonar.duplications.token.Token;
+import org.sonar.duplications.token.TokenQueue;
+
+public class ExactTokenMatcherTest {
+
+ @Test(expected = IllegalArgumentException.class)
+ public void shouldNotAcceptNull() {
+ new ExactTokenMatcher(null);
+ }
+
+ @Test
+ public void shouldMatch() {
+ Token t1 = new Token("a", 1, 1);
+ Token t2 = new Token("b", 2, 1);
+ TokenQueue tokenQueue = spy(new TokenQueue(Arrays.asList(t1, t2)));
+ List<Token> output = mock(List.class);
+ ExactTokenMatcher matcher = new ExactTokenMatcher("a");
+
+ assertThat(matcher.matchToken(tokenQueue, output), is(true));
+ verify(tokenQueue).isNextTokenValue("a");
+ verify(tokenQueue).poll();
+ verifyNoMoreInteractions(tokenQueue);
+ verify(output).add(t1);
+ verifyNoMoreInteractions(output);
+ }
+
+ @Test
+ public void shouldNotMatch() {
+ Token t1 = new Token("a", 1, 1);
+ Token t2 = new Token("b", 2, 1);
+ TokenQueue tokenQueue = spy(new TokenQueue(Arrays.asList(t1, t2)));
+ List<Token> output = mock(List.class);
+ ExactTokenMatcher matcher = new ExactTokenMatcher("b");
+
+ assertThat(matcher.matchToken(tokenQueue, output), is(false));
+ verify(tokenQueue).isNextTokenValue("b");
+ verifyNoMoreInteractions(tokenQueue);
+ verifyNoMoreInteractions(output);
+ }
+
+}
diff --git a/sonar-duplications/src/test/java/org/sonar/duplications/statement/matcher/ForgetLastTokenMatcherTest.java b/sonar-duplications/src/test/java/org/sonar/duplications/statement/matcher/ForgetLastTokenMatcherTest.java
new file mode 100644
index 00000000000..1050e7a10a3
--- /dev/null
+++ b/sonar-duplications/src/test/java/org/sonar/duplications/statement/matcher/ForgetLastTokenMatcherTest.java
@@ -0,0 +1,52 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2011 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar 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.
+ *
+ * Sonar 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 Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.duplications.statement.matcher;
+
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import java.util.List;
+
+import org.junit.Test;
+import org.sonar.duplications.token.Token;
+import org.sonar.duplications.token.TokenQueue;
+
+public class ForgetLastTokenMatcherTest {
+
+ @Test
+ public void shouldMatch() {
+ TokenQueue tokenQueue = spy(new TokenQueue());
+ List<Token> output = mock(List.class);
+ when(output.size()).thenReturn(4);
+ ForgetLastTokenMatcher matcher = new ForgetLastTokenMatcher();
+
+ assertThat(matcher.matchToken(tokenQueue, output), is(true));
+ verifyNoMoreInteractions(tokenQueue);
+ verify(output).size();
+ verify(output).remove(3);
+ verifyNoMoreInteractions(output);
+ }
+
+}
diff --git a/sonar-duplications/src/test/java/org/sonar/duplications/statement/matcher/OptTokenMatcherTest.java b/sonar-duplications/src/test/java/org/sonar/duplications/statement/matcher/OptTokenMatcherTest.java
new file mode 100644
index 00000000000..acddf6df379
--- /dev/null
+++ b/sonar-duplications/src/test/java/org/sonar/duplications/statement/matcher/OptTokenMatcherTest.java
@@ -0,0 +1,56 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2011 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar 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.
+ *
+ * Sonar 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 Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.duplications.statement.matcher;
+
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+
+import java.util.List;
+
+import org.junit.Test;
+import org.sonar.duplications.token.Token;
+import org.sonar.duplications.token.TokenQueue;
+
+public class OptTokenMatcherTest {
+
+ @Test(expected = IllegalArgumentException.class)
+ public void shouldNotAcceptNull() {
+ new OptTokenMatcher(null);
+ }
+
+ @Test
+ public void shouldMatch() {
+ TokenQueue tokenQueue = spy(new TokenQueue());
+ TokenMatcher delegate = mock(TokenMatcher.class);
+ OptTokenMatcher matcher = new OptTokenMatcher(delegate);
+ List<Token> output = mock(List.class);
+
+ assertThat(matcher.matchToken(tokenQueue, output), is(true));
+ verify(delegate).matchToken(tokenQueue, output);
+ verifyNoMoreInteractions(delegate);
+ verifyNoMoreInteractions(tokenQueue);
+ verifyNoMoreInteractions(output);
+ }
+
+}
diff --git a/sonar-duplications/src/test/java/org/sonar/duplications/statement/matcher/UptoTokenMatcherTest.java b/sonar-duplications/src/test/java/org/sonar/duplications/statement/matcher/UptoTokenMatcherTest.java
new file mode 100644
index 00000000000..917c3948043
--- /dev/null
+++ b/sonar-duplications/src/test/java/org/sonar/duplications/statement/matcher/UptoTokenMatcherTest.java
@@ -0,0 +1,106 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2011 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar 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.
+ *
+ * Sonar 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 Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.duplications.statement.matcher;
+
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.junit.Test;
+import org.sonar.duplications.token.Token;
+import org.sonar.duplications.token.TokenQueue;
+
+public class UptoTokenMatcherTest {
+
+ @Test(expected = IllegalArgumentException.class)
+ public void shouldNotAcceptNull() {
+ new UptoTokenMatcher(null);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void shouldNotAcceptEmpty() {
+ new UptoTokenMatcher(new String[] {});
+ }
+
+ @Test
+ public void shouldMatch() {
+ Token t1 = new Token("a", 1, 1);
+ Token t2 = new Token(";", 2, 1); // should stop on this token
+ Token t3 = new Token(";", 3, 1);
+ TokenQueue tokenQueue = spy(new TokenQueue(Arrays.asList(t1, t2, t3)));
+ List<Token> output = mock(List.class);
+ UptoTokenMatcher matcher = new UptoTokenMatcher(new String[] { ";" });
+
+ assertThat(matcher.matchToken(tokenQueue, output), is(true));
+ verify(tokenQueue, times(2)).poll();
+ verify(tokenQueue).peek();
+ verifyNoMoreInteractions(tokenQueue);
+ verify(output).add(t1);
+ verify(output).add(t2);
+ verifyNoMoreInteractions(output);
+ }
+
+ @Test
+ public void shouldMatchAnyOfProvidedTokens() {
+ Token t1 = new Token("a", 1, 1);
+ Token t2 = new Token("{", 2, 1);
+ Token t3 = new Token("b", 3, 1);
+ Token t4 = new Token("}", 4, 1);
+ TokenQueue tokenQueue = spy(new TokenQueue(Arrays.asList(t1, t2, t3, t4)));
+ List<Token> output = mock(List.class);
+ UptoTokenMatcher matcher = new UptoTokenMatcher(new String[] { "{", "}" });
+
+ assertThat(matcher.matchToken(tokenQueue, output), is(true));
+ assertThat(matcher.matchToken(tokenQueue, output), is(true));
+ verify(tokenQueue, times(4)).poll();
+ verify(tokenQueue, times(2)).peek();
+ verifyNoMoreInteractions(tokenQueue);
+ verify(output).add(t1);
+ verify(output).add(t2);
+ verify(output).add(t3);
+ verify(output).add(t4);
+ verifyNoMoreInteractions(output);
+ }
+
+ @Test
+ public void shouldNotMatch() {
+ Token t1 = new Token("a", 1, 1);
+ Token t2 = new Token("b", 2, 1);
+ TokenQueue tokenQueue = spy(new TokenQueue(Arrays.asList(t1, t2)));
+ List<Token> output = mock(List.class);
+ UptoTokenMatcher matcher = new UptoTokenMatcher(new String[] { ";" });
+
+ assertThat(matcher.matchToken(tokenQueue, output), is(false));
+ verify(tokenQueue, times(2)).poll();
+ verify(tokenQueue, times(2)).peek();
+ verifyNoMoreInteractions(tokenQueue);
+ verify(output).add(t1);
+ verify(output).add(t2);
+ verifyNoMoreInteractions(output);
+ }
+
+}
diff --git a/sonar-duplications/src/test/java/org/sonar/duplications/token/BlackHoleTokenChannelTest.java b/sonar-duplications/src/test/java/org/sonar/duplications/token/BlackHoleTokenChannelTest.java
new file mode 100644
index 00000000000..7f540256541
--- /dev/null
+++ b/sonar-duplications/src/test/java/org/sonar/duplications/token/BlackHoleTokenChannelTest.java
@@ -0,0 +1,44 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2011 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar 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.
+ *
+ * Sonar 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 Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.duplications.token;
+
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verifyZeroInteractions;
+
+import org.junit.Test;
+import org.sonar.channel.CodeReader;
+
+public class BlackHoleTokenChannelTest {
+
+ @Test
+ public void shouldConsume() {
+ BlackHoleTokenChannel channel = new BlackHoleTokenChannel("ABC");
+ TokenQueue output = mock(TokenQueue.class);
+ CodeReader codeReader = new CodeReader("ABCD");
+
+ assertThat(channel.consume(codeReader, output), is(true));
+ assertThat(codeReader.getLinePosition(), is(1));
+ assertThat(codeReader.getColumnPosition(), is(3));
+ verifyZeroInteractions(output);
+ }
+
+}
diff --git a/sonar-duplications/src/test/java/org/sonar/duplications/token/TokenChannelTest.java b/sonar-duplications/src/test/java/org/sonar/duplications/token/TokenChannelTest.java
new file mode 100644
index 00000000000..17baea189b9
--- /dev/null
+++ b/sonar-duplications/src/test/java/org/sonar/duplications/token/TokenChannelTest.java
@@ -0,0 +1,92 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2011 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar 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.
+ *
+ * Sonar 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 Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.duplications.token;
+
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.verifyZeroInteractions;
+
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.sonar.channel.CodeReader;
+
+public class TokenChannelTest {
+
+ @Test
+ public void shouldConsume() {
+ TokenChannel channel = new TokenChannel("ABC");
+ TokenQueue output = mock(TokenQueue.class);
+ CodeReader codeReader = new CodeReader("ABCD");
+
+ assertThat(channel.consume(codeReader, output), is(true));
+ ArgumentCaptor<Token> token = ArgumentCaptor.forClass(Token.class);
+ verify(output).add(token.capture());
+ assertThat(token.getValue(), is(new Token("ABC", 1, 0)));
+ verifyNoMoreInteractions(output);
+ assertThat(codeReader.getLinePosition(), is(1));
+ assertThat(codeReader.getColumnPosition(), is(3));
+ }
+
+ @Test
+ public void shouldNormalize() {
+ TokenChannel channel = new TokenChannel("ABC", "normalized");
+ TokenQueue output = mock(TokenQueue.class);
+ CodeReader codeReader = new CodeReader("ABCD");
+
+ assertThat(channel.consume(codeReader, output), is(true));
+ ArgumentCaptor<Token> token = ArgumentCaptor.forClass(Token.class);
+ verify(output).add(token.capture());
+ assertThat(token.getValue(), is(new Token("normalized", 1, 0)));
+ verifyNoMoreInteractions(output);
+ assertThat(codeReader.getLinePosition(), is(1));
+ assertThat(codeReader.getColumnPosition(), is(3));
+ }
+
+ @Test
+ public void shouldNotConsume() {
+ TokenChannel channel = new TokenChannel("ABC");
+ TokenQueue output = mock(TokenQueue.class);
+ CodeReader codeReader = new CodeReader("123");
+
+ assertThat(channel.consume(new CodeReader("123"), output), is(false));
+ verifyZeroInteractions(output);
+ assertThat(codeReader.getLinePosition(), is(1));
+ assertThat(codeReader.getColumnPosition(), is(0));
+ }
+
+ @Test
+ public void shouldCorrectlyDeterminePositionWhenTokenSpansMultipleLines() {
+ TokenChannel channel = new TokenChannel("AB\nC");
+ TokenQueue output = mock(TokenQueue.class);
+ CodeReader codeReader = new CodeReader("AB\nCD");
+
+ assertThat(channel.consume(codeReader, output), is(true));
+ ArgumentCaptor<Token> token = ArgumentCaptor.forClass(Token.class);
+ verify(output).add(token.capture());
+ assertThat(token.getValue(), is(new Token("AB\nC", 1, 0)));
+ verifyNoMoreInteractions(output);
+ assertThat(codeReader.getLinePosition(), is(2));
+ assertThat(codeReader.getColumnPosition(), is(1));
+ }
+
+}
diff --git a/sonar-duplications/src/test/java/org/sonar/duplications/token/TokenChunkerTest.java b/sonar-duplications/src/test/java/org/sonar/duplications/token/TokenChunkerTest.java
new file mode 100644
index 00000000000..d9a421ac4d0
--- /dev/null
+++ b/sonar-duplications/src/test/java/org/sonar/duplications/token/TokenChunkerTest.java
@@ -0,0 +1,46 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2011 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar 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.
+ *
+ * Sonar 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 Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.duplications.token;
+
+import org.junit.Test;
+
+public class TokenChunkerTest {
+
+ /**
+ * In fact this test does not guarantee that we will be able to consume even more great comments,
+ * because {@link org.sonar.channel.CodeBuffer} does not expand dynamically - see issue SONAR-2632.
+ * But at least guarantees that we able to consume source files from JDK 1.6,
+ * because buffer capacity has been increased in comparison with default value,
+ * which is {@link org.sonar.channel.CodeReaderConfiguration#DEFAULT_BUFFER_CAPACITY}.
+ */
+ @Test(timeout = 5000)
+ public void shouldConsumeBigComments() {
+ int capacity = 80000;
+ StringBuilder sb = new StringBuilder(capacity);
+ sb.append("/");
+ for (int i = 3; i < capacity; i++) {
+ sb.append('*');
+ }
+ sb.append("/");
+ TokenChunker chunker = TokenChunker.builder().token("/.*/", "LITERAL").build();
+ chunker.chunk(sb.toString());
+ }
+
+}
diff --git a/sonar-duplications/src/test/java/org/sonar/duplications/token/TokenQueueTest.java b/sonar-duplications/src/test/java/org/sonar/duplications/token/TokenQueueTest.java
new file mode 100644
index 00000000000..bdb6ba00c58
--- /dev/null
+++ b/sonar-duplications/src/test/java/org/sonar/duplications/token/TokenQueueTest.java
@@ -0,0 +1,68 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2011 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar 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.
+ *
+ * Sonar 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 Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.duplications.token;
+
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Before;
+import org.junit.Test;
+
+public class TokenQueueTest {
+
+ TokenQueue tokenQueue;
+
+ @Before
+ public void initTest() {
+ List<Token> tokenList = new ArrayList<Token>();
+ tokenList.add(new Token("a", 1, 0));
+ tokenList.add(new Token("bc", 1, 2));
+ tokenList.add(new Token("def", 1, 5));
+ tokenQueue = new TokenQueue(tokenList);
+ }
+
+ @Test
+ public void shouldPeekToken() {
+ Token token = tokenQueue.peek();
+ assertThat(token, is(new Token("a", 1, 0)));
+ assertThat(tokenQueue.size(), is(3));
+ }
+
+ @Test
+ public void shouldPollToken() {
+ Token token = tokenQueue.poll();
+ assertThat(token, is(new Token("a", 1, 0)));
+ assertThat(tokenQueue.size(), is(2));
+ }
+
+ @Test
+ public void shouldPushTokenAtBegining() {
+ Token pushedToken = new Token("push", 1, 0);
+ List<Token> pushedTokenList = new ArrayList<Token>();
+ pushedTokenList.add(pushedToken);
+ tokenQueue.pushForward(pushedTokenList);
+ assertThat(tokenQueue.peek(), is(pushedToken));
+ assertThat(tokenQueue.size(), is(4));
+ }
+
+}
diff --git a/sonar-duplications/src/test/java/org/sonar/duplications/token/TokenTest.java b/sonar-duplications/src/test/java/org/sonar/duplications/token/TokenTest.java
new file mode 100644
index 00000000000..9e4e7785d19
--- /dev/null
+++ b/sonar-duplications/src/test/java/org/sonar/duplications/token/TokenTest.java
@@ -0,0 +1,49 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2011 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar 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.
+ *
+ * Sonar 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 Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.duplications.token;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertThat;
+
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.not;
+
+public class TokenTest {
+
+ @Test
+ public void shouldBeEqual() {
+ Token firstToken = new Token("MyValue", 1, 3);
+ Token secondToken = new Token("MyValue", 1, 3);
+
+ assertThat(firstToken, is(secondToken));
+ }
+
+ @Test
+ public void shouldNotBeEqual() {
+ Token firstToken = new Token("MyValue", 1, 3);
+ Token secondToken = new Token("MySecondValue", 1, 3);
+ Token thirdToken = new Token("MyValue", 3, 3);
+
+ assertThat(firstToken, not(is(secondToken)));
+ assertThat(firstToken, not(is(thirdToken)));
+ }
+
+}
diff --git a/sonar-duplications/src/test/java/org/sonar/duplications/utils/FastStringComparatorTest.java b/sonar-duplications/src/test/java/org/sonar/duplications/utils/FastStringComparatorTest.java
new file mode 100644
index 00000000000..ddc050ce2bc
--- /dev/null
+++ b/sonar-duplications/src/test/java/org/sonar/duplications/utils/FastStringComparatorTest.java
@@ -0,0 +1,70 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2011 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar 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.
+ *
+ * Sonar 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 Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.duplications.utils;
+
+import static org.hamcrest.Matchers.greaterThan;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.lessThan;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+import org.sonar.duplications.utils.FastStringComparator;
+
+public class FastStringComparatorTest {
+
+ @Test
+ public void sameHashCode() {
+ // Next two Strings have same hash code in Java - see http://www.drmaciver.com/2008/07/javalangstringhashcode/
+ String s1 = "Od";
+ String s2 = "PE";
+ assertTrue("same hash code", s1.hashCode() == s2.hashCode());
+ assertThat("s1 < s2", FastStringComparator.INSTANCE.compare(s1, s2), lessThan(0));
+ assertThat("s2 > s1", FastStringComparator.INSTANCE.compare(s2, s1), greaterThan(0));
+ }
+
+ @Test
+ public void differentHashCode() {
+ String s1 = "a";
+ String s2 = "c";
+ assertTrue("different hash code", s1.hashCode() != s2.hashCode());
+ assertThat("s1 < s2", FastStringComparator.INSTANCE.compare(s1, s2), is(-1));
+ assertThat("s2 > s1", FastStringComparator.INSTANCE.compare(s2, s1), is(1));
+ }
+
+ @Test
+ public void sameObject() {
+ String s1 = "a";
+ String s2 = s1;
+ assertTrue("same objects", s1 == s2);
+ assertThat("s1 = s2", FastStringComparator.INSTANCE.compare(s1, s2), is(0));
+ assertThat("s2 = s1", FastStringComparator.INSTANCE.compare(s2, s1), is(0));
+ }
+
+ @Test
+ public void sameString() {
+ String s1 = new String("a");
+ String s2 = new String("a");
+ assertTrue("different objects", s1 != s2);
+ assertThat("s1 = s2", FastStringComparator.INSTANCE.compare(s1, s2), is(0));
+ assertThat("s2 = s1", FastStringComparator.INSTANCE.compare(s2, s1), is(0));
+ }
+
+}
diff --git a/sonar-duplications/src/test/java/org/sonar/duplications/utils/SortedListsUtilsTest.java b/sonar-duplications/src/test/java/org/sonar/duplications/utils/SortedListsUtilsTest.java
new file mode 100644
index 00000000000..cf099af6eab
--- /dev/null
+++ b/sonar-duplications/src/test/java/org/sonar/duplications/utils/SortedListsUtilsTest.java
@@ -0,0 +1,60 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2011 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar 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.
+ *
+ * Sonar 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 Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.duplications.utils;
+
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.List;
+
+import org.junit.Test;
+
+public class SortedListsUtilsTest {
+
+ @Test
+ public void testContains() {
+ List<Integer> c1 = Arrays.asList(1, 2, 3);
+ List<Integer> c2 = Arrays.asList(1, 2);
+ List<Integer> c3 = Arrays.asList(1, 3);
+
+ assertThat(SortedListsUtils.contains(c1, c1, IntegerComparator.INSTANCE), is(true));
+ assertThat(SortedListsUtils.contains(c1, c2, IntegerComparator.INSTANCE), is(true));
+ assertThat(SortedListsUtils.contains(c1, c3, IntegerComparator.INSTANCE), is(true));
+
+ assertThat(SortedListsUtils.contains(c2, c1, IntegerComparator.INSTANCE), is(false));
+ assertThat(SortedListsUtils.contains(c2, c2, IntegerComparator.INSTANCE), is(true));
+ assertThat(SortedListsUtils.contains(c2, c3, IntegerComparator.INSTANCE), is(false));
+
+ assertThat(SortedListsUtils.contains(c3, c1, IntegerComparator.INSTANCE), is(false));
+ assertThat(SortedListsUtils.contains(c3, c2, IntegerComparator.INSTANCE), is(false));
+ assertThat(SortedListsUtils.contains(c3, c3, IntegerComparator.INSTANCE), is(true));
+ }
+
+ private static class IntegerComparator implements Comparator<Integer> {
+ public static final IntegerComparator INSTANCE = new IntegerComparator();
+
+ public int compare(Integer o1, Integer o2) {
+ return o1 - o2;
+ }
+ }
+
+}