Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Design issue sensor #11

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ target/
META-INF/
bin/
build.properties

.idea/
*.iml
6 changes: 1 addition & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,7 @@ this will run all metrics. At the very least, Saikuro/Cane and Hotspots metrics
* Code Violations

##Giving Credit
The github project [pica/ruby-sonar-plugin](https://github.com/pica/ruby-sonar-plugin), is where the ruby-sonar-plugin started, rather than reinvent the wheel, we thought it better to enhance it.
We used that plugin as a starting point for basic stats, then, updated the references to their latest versions and added additional metrics like line-by-line code coverage and code complexity.

We referenced the [javascript sonar plugin](https://github.com/SonarCommunity/sonar-javascript) and the [php sonar plugin](https://github.com/SonarCommunity/sonar-php) for complexity and coverage implementation.
Our complexity sensor and code coverage sensor borrow heavily from the javascript plugin's equivalent sensors.
This is forked from GoDaddy-Hosting/ruby-sonar-plugin.git, enhanced it by adding sensors for design violations provides by roodi. Fixed bugs to make it compatible with different types of simplecov-rcov reports, populate complexity.

##Tool Versions
This plugin has been tested with the following dependency versions
Expand Down
41 changes: 29 additions & 12 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<sonar.buildVersion>4.5.2</sonar.buildVersion>
<sonar.buildVersion>5.0.1</sonar.buildVersion>
<jdk.min.version>1.6</jdk.min.version>
<jacoco-maven-version>0.7.4.201502262128</jacoco-maven-version>
</properties>
Expand All @@ -24,18 +24,37 @@
<artifactId>snakeyaml</artifactId>
<version>1.16</version>
</dependency>

<dependency>
<groupId>org.codehaus.sonar</groupId>
<artifactId>sonar-plugin-api</artifactId>
<version>${sonar.buildVersion}</version>
</dependency>
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-analyzers-common</artifactId>
<version>4.3.0</version>
</dependency>
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-queryparser</artifactId>
<version>4.3.0</version>
</dependency>
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-core</artifactId>
<version>4.3.0</version>
</dependency>
<dependency>
<groupId>org.codehaus.sonar</groupId>
<artifactId>sonar-batch</artifactId>
<version>${sonar.buildVersion}</version>
<exclusions>
<exclusion>
<groupId>com.googlecode.json-simple</groupId>
<artifactId>json-simple</artifactId>
</exclusion>
</exclusions>
</dependency>

<!-- unit tests -->
<dependency>
<groupId>org.codehaus.sonar</groupId>
Expand All @@ -55,12 +74,6 @@
<version>3.3.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.googlecode.json-simple</groupId>
<artifactId>json-simple</artifactId>
<version>1.1.1</version>
</dependency>

<dependency>
<groupId>org.easytesting</groupId>
<artifactId>fest-assert</artifactId>
Expand All @@ -83,15 +96,19 @@
<artifactId>commons-configuration</artifactId>
<version>1.9</version>
</dependency>

<dependency>
<groupId>com.googlecode.json-simple</groupId>
<artifactId>json-simple</artifactId>
<version>1.1.1</version>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.codehaus.sonar</groupId>
<artifactId>sonar-packaging-maven-plugin</artifactId>
<version>1.7</version>
<version>1.8</version>
<extensions>true</extensions>
<configuration>
<pluginClass>com.godaddy.sonar.ruby.RubyPlugin</pluginClass>
Expand Down Expand Up @@ -201,7 +218,7 @@
<ignore></ignore>
</action>
</pluginExecution>
</pluginExecutions>
</pluginExecutions>f
</lifecycleMappingMetadata>
</configuration>
</plugin>
Expand Down
28 changes: 20 additions & 8 deletions src/main/java/com/godaddy/sonar/ruby/RubyPlugin.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@
import java.util.Arrays;
import java.util.List;

import com.godaddy.sonar.ruby.metricfu.MetricfuRoodiYamlParserImpl;
import com.godaddy.sonar.ruby.metricfu.rules.RoodiRuleParser;
import com.godaddy.sonar.ruby.metricfu.rules.RoodiSensor;
import com.godaddy.sonar.ruby.metricfu.rules.RubyRuleRepository;
import org.sonar.api.CoreProperties;
import org.sonar.api.Properties;
import org.sonar.api.PropertyType;
Expand Down Expand Up @@ -32,13 +36,14 @@ public final class RubyPlugin extends SonarPlugin
public List<Object> getExtensions()
{
List<Object> extensions = new ArrayList<Object>();
extensions.add(Ruby.class);
extensions.add(SimpleCovRcovSensor.class);
extensions.add(SimpleCovRcovJsonParserImpl.class);
extensions.add(MetricfuComplexityYamlParserImpl.class);
extensions.add(RubySourceCodeColorizer.class);
extensions.add(RubySensor.class);
extensions.add(MetricfuComplexitySensor.class);

extensions.add(Ruby.class);
extensions.add(SimpleCovRcovSensor.class);
extensions.add(SimpleCovRcovJsonParserImpl.class);
extensions.add(MetricfuComplexityYamlParserImpl.class);
extensions.add(RubySourceCodeColorizer.class);
extensions.add(RubySensor.class);
extensions.add(MetricfuComplexitySensor.class);

// Profiles
extensions.add(SonarWayProfile.class);
Expand Down Expand Up @@ -70,13 +75,20 @@ public List<Object> getExtensions()
.subCategory("Ruby Coverage")
.name("MetricFu Complexity Metric")
.description("Type of complexity, Saikuro or Cane")
.defaultValue("Saikuro")
.defaultValue("Cane")
.onQualifiers(Qualifiers.PROJECT)
.type(PropertyType.SINGLE_SELECT_LIST)
.options(options)
.build();
extensions.add(ComplexityMetric);

extensions.add(RubyRuleRepository.class);

extensions.add(MetricfuRoodiYamlParserImpl.class);
extensions.add(RoodiSensor.class);
// extensions.add(MetricfuDuplicationSensor.class);
// extensions.add(MetricfuDuplicationYamlParserImpl.class);

return extensions;
}
}
1 change: 0 additions & 1 deletion src/main/java/com/godaddy/sonar/ruby/RubySensor.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
import org.sonar.api.batch.Sensor;
import org.sonar.api.batch.SensorContext;
import org.sonar.api.batch.fs.FilePredicate;
import org.sonar.api.batch.fs.FilePredicates;
import org.sonar.api.batch.fs.FileSystem;
import org.sonar.api.batch.fs.InputFile;
import org.sonar.api.config.Settings;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ public boolean shouldExecuteOnProject(Project project)
return fs.hasFiles(fs.predicates().hasLanguage("ruby"));
}

@Override
public void analyse(Project project, SensorContext context)
{
File report = pathResolver.relativeFile(fs.baseDir(), reportPath);
Expand Down Expand Up @@ -100,7 +101,7 @@ private void analyzeFile(InputFile inputFile, SensorContext sensorContext, File
fileComplexity += function.getComplexity();
LOG.info("File complexity " + fileComplexity);
}

sensorContext.saveMeasure(inputFile, CoreMetrics.COMPLEXITY, Double.valueOf(fileComplexity));
RangeDistributionBuilder fileDistribution = new RangeDistributionBuilder(CoreMetrics.FILE_COMPLEXITY_DISTRIBUTION,
FILES_DISTRIB_BOTTOM_LIMITS);
fileDistribution.add(Double.valueOf(fileComplexity));
Expand All @@ -115,6 +116,7 @@ private void analyzeFile(InputFile inputFile, SensorContext sensorContext, File
{
functionDistribution.add(Double.valueOf(function.getComplexity()));
}
System.out.println("Analyzeds for : "+ inputFile);
sensorContext.saveMeasure(inputFile, functionDistribution.build().setPersistenceMode(PersistenceMode.MEMORY));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@

public class MetricfuComplexityYamlParserImpl implements
MetricfuComplexityYamlParser {

private Map<String, Object> metricfuResult;
private static final Logger LOG = LoggerFactory
.getLogger(MetricfuComplexityYamlParser.class);

Expand All @@ -44,10 +46,12 @@ public List<RubyFunction> parseFunctions(String fileNameFromModule, File results
// }

Yaml yaml = new Yaml();
Map<String, Object> metricfuResult = new HashMap();
try {
// metricfuResult = (Map<String, Object>) yaml.loadAs(fileString, Map.class);
metricfuResult = (Map<String, Object>) yaml.load(resultsStream);
if(metricfuResult == null) {
metricfuResult = new HashMap();
metricfuResult = (Map<String, Object>) yaml.load(resultsStream);
}

Map<String, Object> saikuroResult = (Map<String, Object>) metricfuResult.get(":saikuro");
Map<String, Object> caneResult = (Map<String, Object>) metricfuResult.get(":cane");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package com.godaddy.sonar.ruby.metricfu;

import com.godaddy.sonar.ruby.RubyPlugin;
import com.google.common.collect.Lists;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonar.api.batch.Sensor;
import org.sonar.api.batch.SensorContext;
import org.sonar.api.batch.fs.FileSystem;
import org.sonar.api.batch.fs.InputFile;
import org.sonar.api.batch.sensor.duplication.DuplicationBuilder;
import org.sonar.api.config.Settings;
import org.sonar.api.resources.Project;
import org.sonar.api.scan.filesystem.PathResolver;

import java.io.File;
import java.io.IOException;
import java.util.List;

/**
* Created by akash.v on 22/04/16.
*/
public class MetricfuDuplicationSensor implements Sensor {

private static final Logger LOG = LoggerFactory
.getLogger(MetricfuComplexitySensor.class);

private MetricfuDuplicationYamlParserImpl metricfuDuplicationYamlParserImpl;
private Settings settings;
private FileSystem fs;

private static final Number[] FILES_DISTRIB_BOTTOM_LIMITS = { 0, 5, 10, 20, 30, 60, 90 };
private static final Number[] FUNCTIONS_DISTRIB_BOTTOM_LIMITS = { 1, 2, 4, 6, 8, 10, 12, 20, 30 };

private String reportPath = "tmp/metric_fu/report.yml";
private PathResolver pathResolver;

public MetricfuDuplicationSensor(Settings settings, FileSystem fs,
PathResolver pathResolver,
MetricfuDuplicationYamlParserImpl metricfuDuplicationYamlParserImpl) {
this.settings = settings;
this.fs = fs;
this.metricfuDuplicationYamlParserImpl = metricfuDuplicationYamlParserImpl;
this.pathResolver = pathResolver;
String reportpath_prop = settings.getString(RubyPlugin.METRICFU_REPORT_PATH_PROPERTY);
if (null != reportpath_prop) {
this.reportPath = reportpath_prop;
}
}

@Override
public void analyse(Project project, SensorContext sensorContext) {
LOG.info("Analysing Duplications.");
File report = pathResolver.relativeFile(fs.baseDir(), reportPath);
LOG.info("Calling analyse for report results: " + report.getPath());
if (!report.isFile()) {
LOG.warn("MetricFu report not found at {}", report);
return;
}

List<InputFile> sourceFiles = Lists.newArrayList(fs.inputFiles(fs.predicates().hasLanguage("ruby")));

try {
metricfuDuplicationYamlParserImpl.parse(sourceFiles, report);
} catch (IOException e) {
LOG.error("Parsing duplications failed:");
e.printStackTrace();
}
}


@Override
public boolean shouldExecuteOnProject(Project project) {
return fs.hasFiles(fs.predicates().hasLanguage("ruby"));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.godaddy.sonar.ruby.metricfu;

import org.sonar.api.BatchExtension;
import org.sonar.api.batch.fs.InputFile;

import java.io.File;
import java.io.IOException;
import java.util.List;

/**
* Created by akash.v on 25/04/16.
*/
public interface MetricfuDuplicationYamlParser extends BatchExtension {

public void parse(List<InputFile> inputFiles, File resultsFile) throws IOException;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package com.godaddy.sonar.ruby.metricfu;

import com.google.common.base.Throwables;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonar.api.batch.fs.InputFile;
import org.yaml.snakeyaml.Yaml;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
* Created by akash.v on 22/04/16.
*/
public class MetricfuDuplicationYamlParserImpl implements MetricfuDuplicationYamlParser {

private static final Logger LOG = LoggerFactory
.getLogger(MetricfuComplexityYamlParser.class);

@SuppressWarnings("unchecked")
@Override
public void parse(List<InputFile> inputFiles, File resultsFile) throws IOException
{
InputStream resultsStream = new FileInputStream(resultsFile);

LOG.debug("MetricfuDuplicationYamlParserImpl: Start start parse of metrics_fu YAML");

Yaml yaml = new Yaml();
Map<String, Map<String, ArrayList>> metricfuResult = new HashMap();
try {
metricfuResult = (Map<String, Map<String, ArrayList>>) yaml.load(resultsStream);

ArrayList<Map<String, Object>> flayResult = metricfuResult.get(":flay").get(":matches");

analyzeFlay(inputFiles, flayResult);

} catch (Exception e) {
LOG.error(Throwables.getStackTraceAsString(e));
throw new IOException("Failure parsing YAML results", e);
}

}

private void analyzeFlay(List<InputFile> inputFiles, ArrayList<Map<String, Object>> flayResult) {
Map<String, InputFile> indexedInputFiles = index(inputFiles);
for(Map<String, Object> duplicate : flayResult){
if(((String)duplicate.get(":reason")).contains("IDENTICAL")){
ArrayList<Map<String, String>> matches = (ArrayList<Map<String, String>>) duplicate.get(":matches");
// DuplicationBuilder duplicationBuilder = new DefaultDuplicationBuilder(indexedInputFiles.get(matches.get(0).get(":name")));
LOG.info("Identical Code Found: {}", matches );
for(Map<String, String> match : matches ){
// duplicationBuilder.isDuplicatedBy(indexedInputFiles.get(match.get(":name")), Integer.parseInt(match.get(":line")), 1);

}
}
}

}

private Map<String, InputFile> index(List<InputFile> inputFiles) {
Map<String, InputFile> indexedFiles= Maps.newConcurrentMap();

for(InputFile inputFile : inputFiles){
indexedFiles.put(inputFile.relativePath(), inputFile);
}

return indexedFiles;
}
}
Loading