/*
* SonarQube
* Copyright (C) 2009-2017 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.sonar.scanner.mediumtest.cpd;
import com.google.common.collect.ImmutableMap;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import org.apache.commons.io.FileUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.rules.TemporaryFolder;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
import org.sonar.api.batch.fs.InputFile;
import org.sonar.api.batch.fs.internal.DefaultInputFile;
import org.sonar.scanner.mediumtest.LogOutputRecorder;
import org.sonar.scanner.mediumtest.ScannerMediumTester;
import org.sonar.scanner.mediumtest.TaskResult;
import org.sonar.scanner.protocol.output.ScannerReport;
import org.sonar.xoo.XooPlugin;
import org.sonar.xoo.lang.CpdTokenizerSensor;
import org.sonar.xoo.rule.XooRulesDefinition;
import static org.assertj.core.api.Assertions.assertThat;
@RunWith(Parameterized.class)
public class CpdMediumTest {
@Parameters(name = "new api: {0}")
public static Collection<Object[]> data() {
return Arrays.asList(new Object[][] {
{true}, {false}
});
}
@Rule
public TemporaryFolder temp = new TemporaryFolder();
@Rule
public ExpectedException thrown = ExpectedException.none();
private LogOutputRecorder logRecorder = new LogOutputRecorder();
public ScannerMediumTester tester = ScannerMediumTester.builder()
.registerPlugin("xoo", new XooPlugin())
.addDefaultQProfile("xoo", "Sonar Way")
.addRules(new XooRulesDefinition())
// active a rule just to be sure that xoo files are published
.addActiveRule("xoo", "xoo:OneIssuePerFile", null, "One Issue Per File", null, null, null)
.setLogOutput(logRecorder)
.build();
private File baseDir;
private ImmutableMap.Builder<String, String> builder;
private boolean useNewSensorApi;
public CpdMediumTest(boolean useNewSensorApi) {
this.useNewSensorApi = useNewSensorApi;
}
@Before
public void prepare() throws IOException {
logRecorder.getAll().clear();
tester.start();
baseDir = temp.getRoot();
builder = ImmutableMap.<String, String>builder()
.put("sonar.task", "scan")
.put("sonar.projectBaseDir", baseDir.getAbsolutePath())
.put("sonar.projectKey", "com.foo.project")
.put("sonar.projectName", "Foo Project")
.put("sonar.projectVersion", "1.0-SNAPSHOT")
.put("sonar.projectDescription", "Description of Foo Project");
if (useNewSensorApi) {
builder.put(CpdTokenizerSensor.ENABLE_PROP, "true");
}
}
@After
public void stop() {
tester.stop();
}
@Test
public void testCrossModuleDuplications() throws IOException {
builder.put("sonar.modules", "module1,module2")
.put("sonar.cpd.xoo.minimumTokens", "10")
.put("sonar.verbose", "true");
// module 1
builder.put("module1.sonar.projectKey", "module1");
builder.put("module1.sonar.projectName", "Module 1");
builder.put("module1.sonar.sources", ".");
// module2
builder.put("module2.sonar.projectKey", "module2");
builder.put("module2.sonar.projectName", "Module 2");
builder.put("module2.sonar.sources", ".");
File module1Dir = new File(baseDir, "module1");
File module2Dir = new File(baseDir, "module2");
module1Dir.mkdir();
module2Dir.mkdir();
String duplicatedStuff = "Sample xoo\ncontent\n"
+ "foo\nbar\ntoto\ntiti\n"
+ "foo\nbar\ntoto\ntiti\n"
+ "bar\ntoto\ntiti\n"
+ "foo\nbar\ntoto\ntiti";
// create duplicated file in both modules
File xooFile1 = new File(module1Dir, "sample1.xoo");
FileUtils.write(xooFile1, duplicatedStuff);
File xooFile2 = new File(module2Dir, "sample2.xoo");
FileUtils.write(xooFile2, duplicatedStuff);
TaskResult result = tester.newTask().properties(builder.build()).start();
assertThat(result.inputFiles()).hasSize(2);
InputFile inputFile1 = result.inputFile("sample1.xoo");
InputFile inputFile2 = result.inputFile("sample2.xoo");
// One clone group on each file
List<org.sonar.scanner.protocol.output.ScannerReport.Duplication> duplicationGroupsFile1 = result.duplicationsFor(inputFile1);
assertThat(duplicationGroupsFile1).hasSize(1);
org.sonar.scanner.protocol.output.ScannerReport.Duplication cloneGroupFile1 = duplicationGroupsFile1.get(0);
assertThat(cloneGroupFile1.getOriginPosition().getStartLine()).isEqualTo(1);
assertThat(cloneGroupFile1.getOriginPosition().getEndLine()).isEqualTo(17);
assertThat(cloneGroupFile1.getDuplicateList()).hasSize(1);
assertThat(cloneGroupFile1.getDuplicate(0).getOtherFileRef()).isEqualTo(result.getReportComponent(((DefaultInputFile) inputFile2).key()).getRef());
List<org.sonar.scanner.protocol.output.ScannerReport.Duplication> duplicationGroupsFile2 = result.duplicationsFor(inputFile2);
assertThat(duplicationGroupsFile2).hasSize(1);
org.sonar.scanner.protocol.output.ScannerReport.Duplication cloneGroupFile2 = duplicationGroupsFile2.get(0);
assertThat(cloneGroupFile2.getOriginPosition().getStartLine()).isEqualTo(1);
assertThat(cloneGroupFile2.getOriginPosition().getEndLine()).isEqualTo(17);
assertThat(cloneGroupFile2.getDuplicateList()).hasSize(1);
assertThat(cloneGroupFile2.getDuplicate(0).getOtherFileRef()).isEqualTo(result.getReportComponent(((DefaultInputFile) inputFile1).key()).getRef());
assertThat(result.duplicationBlocksFor(inputFile1)).isEmpty();
}
@Test
public void testCrossFileDuplications() throws IOException {
File srcDir = new File(baseDir, "src");
srcDir.mkdir();
String duplicatedStuff = "Sample xoo\ncontent\n"
+ "foo\nbar\ntoto\ntiti\n"
+ "foo\nbar\ntoto\ntiti\n"
+ "bar\ntoto\ntiti\n"
+ "foo\nbar\ntoto\ntiti";
File xooFile1 = new File(srcDir, "sample1.xoo");
FileUtils.write(xooFile1, duplicatedStuff);
File xooFile2 = new File(srcDir, "sample2.xoo");
FileUtils.write(xooFile2, duplicatedStuff);
TaskResult result = tester.newTask()
.properties(builder
.put("sonar.sources", "src")
.put("sonar.cpd.xoo.minimumTokens", "10")
.put("sonar.verbose", "true")
.build())
.start();
assertThat(result.inputFiles()).hasSize(2);
InputFile inputFile1 = result.inputFile("src/sample1.xoo");
InputFile inputFile2 = result.inputFile("src/sample2.xoo");
// One clone group on each file
List<org.sonar.scanner.protocol.output.ScannerReport.Duplication> duplicationGroupsFile1 = result.duplicationsFor(inputFile1);
assertThat(duplicationGroupsFile1).hasSize(1);
org.sonar.scanner.protocol.output.ScannerReport.Duplication cloneGroupFile1 = duplicationGroupsFile1.get(0);
assertThat(cloneGroupFile1.getOriginPosition().getStartLine()).isEqualTo(1);
assertThat(cloneGroupFile1.getOriginPosition().getEndLine()).isEqualTo(17);
assertThat(cloneGroupFile1.getDuplicateList()).hasSize(1);
assertThat(cloneGroupFile1.getDuplicate(0).getOtherFileRef()).isEqualTo(result.getReportComponent(((DefaultInputFile) inputFile2).key()).getRef());
List<org.sonar.scanner.protocol.output.ScannerReport.Duplication> duplicationGroupsFile2 = result.duplicationsFor(inputFile2);
assertThat(duplicationGroupsFile2).hasSize(1);
org.sonar.scanner.protocol.output.ScannerReport.Duplication cloneGroupFile2 = duplicationGroupsFile2.get(0);
assertThat(cloneGroupFile2.getOriginPosition().getStartLine()).isEqualTo(1);
assertThat(cloneGroupFile2.getOriginPosition().getEndLine()).isEqualTo(17);
assertThat(cloneGroupFile2.getDuplicateList()).hasSize(1);
assertThat(cloneGroupFile2.getDuplicate(0).getOtherFileRef()).isEqualTo(result.getReportComponent(((DefaultInputFile) inputFile1).key()).getRef());
assertThat(result.duplicationBlocksFor(inputFile1)).isEmpty();
}
@Test
public void testFilesWithoutBlocks() throws IOException {
File srcDir = new File(baseDir, "src");
srcDir.mkdir();
String file1 = "Sample xoo\ncontent\n"
+ "foo\nbar\ntoto\ntiti\n"
+ "foo\nbar\ntoto\ntiti\n"
+ "bar\ntoto\ntiti\n"
+ "foo\nbar\ntoto\ntiti";
String file2 = "string\n";
File xooFile1 = new File(srcDir, "sample1.xoo");
FileUtils.write(xooFile1, file1);
File xooFile2 = new File(srcDir, "sample2.xoo");
FileUtils.write(xooFile2, file2);
TaskResult result = tester.newTask()
.properties(builder
.put("sonar.sources", "src")
.put("sonar.cpd.xoo.minimumTokens", "10")
.put("sonar.verbose", "true")
.build())
.start();
assertThat(result.inputFiles()).hasSize(2);
assertThat(logRecorder.getAllAsString()).contains("Not enough content in 'src/sample2.xoo' to have CPD blocks");
assertThat(logRecorder.getAllAsString()).contains("1 file had no CPD blocks");
}
@Test
public void testExclusions() throws IOException {
File srcDir = new File(baseDir, "src");
srcDir.mkdir();
String duplicatedStuff = "Sample xoo\ncontent\n"
+ "foo\nbar\ntoto\ntiti\n"
+ "foo\nbar\ntoto\ntiti\n"
+ "bar\ntoto\ntiti\n"
+ "foo\nbar\ntoto\ntiti";
File xooFile1 = new File(srcDir, "sample1.xoo");
FileUtils.write(xooFile1, duplicatedStuff);
File xooFile2 = new File(srcDir, "sample2.xoo");
FileUtils.write(xooFile2, duplicatedStuff);
TaskResult result = tester.newTask()
.properties(builder
.put("sonar.sources", "src")
.put("sonar.cpd.xoo.minimumTokens", "10")
.put("sonar.cpd.exclusions", "src/sample1.xoo")
.build())
.start();
assertThat(result.inputFiles()).hasSize(2);
InputFile inputFile1 = result.inputFile("src/sample1.xoo");
InputFile inputFile2 = result.inputFile("src/sample2.xoo");
List<org.sonar.scanner.protocol.output.ScannerReport.Duplication> duplicationGroupsFile1 = result.duplicationsFor(inputFile1);
assertThat(duplicationGroupsFile1).isEmpty();
List<org.sonar.scanner.protocol.output.ScannerReport.Duplication> duplicationGroupsFile2 = result.duplicationsFor(inputFile2);
assertThat(duplicationGroupsFile2).isEmpty();
}
@Test
public void enableCrossProjectDuplication() throws IOException {
File srcDir = new File(baseDir, "src");
srcDir.mkdir();
String duplicatedStuff = "Sample xoo\ncontent\nfoo\nbar\ntoto\ntiti\nfoo";
File xooFile1 = new File(srcDir, "sample1.xoo");
FileUtils.write(xooFile1, duplicatedStuff);
TaskResult result = tester.newTask()
.properties(builder
.put("sonar.sources", "src")
.put("sonar.cpd.xoo.minimumTokens", "1")
.put("sonar.cpd.xoo.minimumLines", "5")
.put("sonar.verbose", "true")
.put("sonar.cpd.cross_project", "true")
.build())
.start();
InputFile inputFile1 = result.inputFile("src/sample1.xoo");
List<ScannerReport.CpdTextBlock> duplicationBlocks = result.duplicationBlocksFor(inputFile1);
assertThat(duplicationBlocks).hasSize(3);
assertThat(duplicationBlocks.get(0).getStartLine()).isEqualTo(1);
assertThat(duplicationBlocks.get(0).getEndLine()).isEqualTo(5);
assertThat(duplicationBlocks.get(0).getStartTokenIndex()).isEqualTo(1);
assertThat(duplicationBlocks.get(0).getEndTokenIndex()).isEqualTo(6);
assertThat(duplicationBlocks.get(0).getHash()).isNotEmpty();
assertThat(duplicationBlocks.get(1).getStartLine()).isEqualTo(2);
assertThat(duplicationBlocks.get(1).getEndLine()).isEqualTo(6);
assertThat(duplicationBlocks.get(1).getStartTokenIndex()).isEqualTo(3);
assertThat(duplicationBlocks.get(1).getEndTokenIndex()).isEqualTo(7);
assertThat(duplicationBlocks.get(0).getHash()).isNotEmpty();
assertThat(duplicationBlocks.get(2).getStartLine()).isEqualTo(3);
assertThat(duplicationBlocks.get(2).getEndLine()).isEqualTo(7);
assertThat(duplicationBlocks.get(2).getStartTokenIndex()).isEqualTo(4);
assertThat(duplicationBlocks.get(2).getEndTokenIndex()).isEqualTo(8);
assertThat(duplicationBlocks.get(0).getHash()).isNotEmpty();
}
@Test
public void testIntraFileDuplications() throws IOException {
File srcDir = new File(baseDir, "src");
srcDir.mkdir();
String content = "Sample xoo\ncontent\nfoo\nbar\nSample xoo\ncontent\n";
File xooFile = new File(srcDir, "sample.xoo");
FileUtils.write(xooFile, content);
TaskResult result = tester.newTask()
.properties(builder
.put("sonar.sources", "src")
.put("sonar.cpd.xoo.minimumTokens", "2")
.put("sonar.cpd.xoo.minimumLines", "2")
.put("sonar.verbose", "true")
.build())
.start();
InputFile inputFile = result.inputFile("src/sample.xoo");
// One clone group
List<org.sonar.scanner.protocol.output.ScannerReport.Duplication> duplicationGroups = result.duplicationsFor(inputFile);
assertThat(duplicationGroups).hasSize(1);
org.sonar.scanner.protocol.output.ScannerReport.Duplication cloneGroup = duplicationGroups.get(0);
assertThat(cloneGroup.getOriginPosition().getStartLine()).isEqualTo(1);
assertThat(cloneGroup.getOriginPosition().getEndLine()).isEqualTo(2);
assertThat(cloneGroup.getDuplicateList()).hasSize(1);
assertThat(cloneGroup.getDuplicate(0).getRange().getStartLine()).isEqualTo(5);
assertThat(cloneGroup.getDuplicate(0).getRange().getEndLine()).isEqualTo(6);
}
}