@@ -23,7 +23,6 @@ import com.google.common.base.Strings; | |||
import java.util.Collection; | |||
import java.util.Map; | |||
import org.sonar.api.issue.Issue; | |||
import org.sonar.api.issue.condition.Condition; | |||
import org.sonar.api.issue.condition.IsUnResolved; | |||
import org.sonar.api.server.ServerSide; | |||
import org.sonar.api.web.UserRole; | |||
@@ -42,12 +41,7 @@ public class SetSeverityAction extends Action { | |||
super(SET_SEVERITY_KEY); | |||
this.issueUpdater = issueUpdater; | |||
this.userSession = userSession; | |||
super.setConditions(new IsUnResolved(), new Condition() { | |||
@Override | |||
public boolean matches(Issue issue) { | |||
return isCurrentUserIssueAdmin(issue.projectKey()); | |||
} | |||
}); | |||
super.setConditions(new IsUnResolved(), issue -> isCurrentUserIssueAdmin(issue.projectKey())); | |||
} | |||
private boolean isCurrentUserIssueAdmin(String projectKey) { |
@@ -20,7 +20,6 @@ | |||
package org.sonar.server.notification; | |||
import com.google.common.annotations.VisibleForTesting; | |||
import com.google.common.base.Function; | |||
import com.google.common.collect.HashMultimap; | |||
import com.google.common.collect.Lists; | |||
import com.google.common.collect.Multimap; | |||
@@ -78,12 +77,7 @@ public class DefaultNotificationManager implements NotificationManager { | |||
@Override | |||
public void scheduleForSending(List<Notification> notification) { | |||
notificationQueueDao.insert(Lists.transform(notification, new Function<Notification, NotificationQueueDto>() { | |||
@Override | |||
public NotificationQueueDto apply(Notification notification) { | |||
return NotificationQueueDto.toNotificationQueueDto(notification); | |||
} | |||
})); | |||
notificationQueueDao.insert(Lists.transform(notification, NotificationQueueDto::toNotificationQueueDto)); | |||
} | |||
/** | |||
@@ -130,7 +124,7 @@ public class DefaultNotificationManager implements NotificationManager { | |||
*/ | |||
@Override | |||
public Multimap<String, NotificationChannel> findSubscribedRecipientsForDispatcher(NotificationDispatcher dispatcher, | |||
@Nullable String projectUuid) { | |||
@Nullable String projectUuid) { | |||
String dispatcherKey = dispatcher.getKey(); | |||
SetMultimap<String, NotificationChannel> recipients = HashMultimap.create(); |
@@ -26,8 +26,9 @@ import com.google.common.collect.ImmutableMap; | |||
import com.google.common.collect.ImmutableSet; | |||
import com.google.common.collect.Maps; | |||
import java.io.Serializable; | |||
import java.time.Instant; | |||
import java.time.temporal.ChronoUnit; | |||
import java.util.ArrayList; | |||
import java.util.Calendar; | |||
import java.util.Collection; | |||
import java.util.Collections; | |||
import java.util.Date; | |||
@@ -41,7 +42,6 @@ import javax.annotation.Nullable; | |||
import org.apache.commons.lang.StringUtils; | |||
import org.apache.commons.lang.builder.ToStringBuilder; | |||
import org.apache.commons.lang.builder.ToStringStyle; | |||
import org.apache.commons.lang.time.DateUtils; | |||
import org.sonar.api.issue.Issue; | |||
import org.sonar.api.issue.IssueComment; | |||
import org.sonar.api.rule.RuleKey; | |||
@@ -306,7 +306,6 @@ public class DefaultIssue implements Issue, Trackable, org.sonar.api.ce.measure. | |||
return effort; | |||
} | |||
@CheckForNull | |||
public Long effortInMinutes() { | |||
return effort != null ? effort.toMinutes() : null; | |||
@@ -366,11 +365,20 @@ public class DefaultIssue implements Issue, Trackable, org.sonar.api.ce.measure. | |||
} | |||
public DefaultIssue setCreationDate(Date d) { | |||
// d is not marked as Nullable but we still allow null parameter for unit testing. | |||
this.creationDate = d != null ? DateUtils.truncate(d, Calendar.SECOND) : null; | |||
this.creationDate = truncateToSeconds(d); | |||
return this; | |||
} | |||
@CheckForNull | |||
private static Date truncateToSeconds(@Nullable Date d) { | |||
if (d == null) { | |||
return null; | |||
} | |||
Instant instant = d.toInstant(); | |||
instant = instant.truncatedTo(ChronoUnit.SECONDS); | |||
return Date.from(instant); | |||
} | |||
@Override | |||
@CheckForNull | |||
public Date updateDate() { | |||
@@ -378,7 +386,7 @@ public class DefaultIssue implements Issue, Trackable, org.sonar.api.ce.measure. | |||
} | |||
public DefaultIssue setUpdateDate(@Nullable Date d) { | |||
this.updateDate = d != null ? DateUtils.truncate(d, Calendar.SECOND) : null; | |||
this.updateDate = truncateToSeconds(d); | |||
return this; | |||
} | |||
@@ -389,7 +397,7 @@ public class DefaultIssue implements Issue, Trackable, org.sonar.api.ce.measure. | |||
} | |||
public DefaultIssue setCloseDate(@Nullable Date d) { | |||
this.closeDate = d != null ? DateUtils.truncate(d, Calendar.SECOND) : null; | |||
this.closeDate = truncateToSeconds(d); | |||
return this; | |||
} | |||
@@ -580,7 +588,7 @@ public class DefaultIssue implements Issue, Trackable, org.sonar.api.ce.measure. | |||
@CheckForNull | |||
public <T> T getLocations() { | |||
return (T)locations; | |||
return (T) locations; | |||
} | |||
public DefaultIssue setLocations(@Nullable Object locations) { |
@@ -19,7 +19,6 @@ | |||
*/ | |||
package org.sonar.api.batch.fs.internal; | |||
import com.google.common.base.Predicate; | |||
import com.google.common.collect.Iterables; | |||
import org.sonar.api.batch.fs.FilePredicate; | |||
import org.sonar.api.batch.fs.FileSystem.Index; | |||
@@ -36,12 +35,7 @@ public abstract class AbstractFilePredicate implements OptimizedFilePredicate { | |||
@Override | |||
public Iterable<InputFile> filter(Iterable<InputFile> target) { | |||
return Iterables.filter(target, new Predicate<InputFile>() { | |||
@Override | |||
public boolean apply(InputFile input) { | |||
return AbstractFilePredicate.this.apply(input); | |||
} | |||
}); | |||
return Iterables.filter(target, this::apply); | |||
} | |||
@Override |
@@ -19,7 +19,6 @@ | |||
*/ | |||
package org.sonar.api.batch.fs.internal; | |||
import com.google.common.base.Function; | |||
import com.google.common.collect.Iterables; | |||
import java.io.File; | |||
import java.io.IOException; | |||
@@ -33,7 +32,6 @@ import java.util.Iterator; | |||
import java.util.Map; | |||
import java.util.SortedSet; | |||
import java.util.TreeSet; | |||
import javax.annotation.CheckForNull; | |||
import javax.annotation.Nullable; | |||
import org.sonar.api.batch.fs.FilePredicate; | |||
import org.sonar.api.batch.fs.FilePredicates; | |||
@@ -172,12 +170,7 @@ public class DefaultFileSystem implements FileSystem { | |||
@Override | |||
public Iterable<File> files(FilePredicate predicate) { | |||
doPreloadFiles(); | |||
return Iterables.transform(inputFiles(predicate), new Function<InputFile, File>() { | |||
@Override | |||
public File apply(InputFile input) { | |||
return input.file(); | |||
} | |||
}); | |||
return Iterables.transform(inputFiles(predicate), InputFile::file); | |||
} | |||
@Override | |||
@@ -249,16 +242,6 @@ public class DefaultFileSystem implements FileSystem { | |||
} | |||
public abstract static class Cache implements Index { | |||
@Override | |||
public abstract Iterable<InputFile> inputFiles(); | |||
@Override | |||
@CheckForNull | |||
public abstract InputFile inputFile(String relativePath); | |||
@Override | |||
@CheckForNull | |||
public abstract InputDir inputDir(String relativePath); | |||
protected abstract void doAdd(InputFile inputFile); | |||
@@ -367,6 +367,7 @@ public class FileMetadata { | |||
} | |||
} | |||
@FunctionalInterface | |||
public interface LineHashConsumer { | |||
void consume(int lineIdx, @Nullable byte[] hash); |
@@ -22,7 +22,6 @@ package org.sonar.api.batch.sensor.highlighting.internal; | |||
import com.google.common.base.Preconditions; | |||
import java.util.ArrayList; | |||
import java.util.Collections; | |||
import java.util.Comparator; | |||
import java.util.Iterator; | |||
import java.util.List; | |||
import org.sonar.api.batch.fs.InputFile; | |||
@@ -109,15 +108,12 @@ public class DefaultHighlighting extends DefaultStorable implements NewHighlight | |||
protected void doSave() { | |||
checkInputFileNotNull(); | |||
// Sort rules to avoid variation during consecutive runs | |||
Collections.sort(syntaxHighlightingRules, new Comparator<SyntaxHighlightingRule>() { | |||
@Override | |||
public int compare(SyntaxHighlightingRule left, SyntaxHighlightingRule right) { | |||
int result = left.range().start().compareTo(right.range().start()); | |||
if (result == 0) { | |||
result = right.range().end().compareTo(left.range().end()); | |||
} | |||
return result; | |||
Collections.sort(syntaxHighlightingRules, (left, right) -> { | |||
int result = left.range().start().compareTo(right.range().start()); | |||
if (result == 0) { | |||
result = right.range().end().compareTo(left.range().end()); | |||
} | |||
return result; | |||
}); | |||
checkOverlappingBoudaries(); | |||
storage.store(this); |
@@ -19,10 +19,8 @@ | |||
*/ | |||
package org.sonar.api.batch.sensor.issue.internal; | |||
import com.google.common.base.Function; | |||
import com.google.common.base.Preconditions; | |||
import com.google.common.collect.ImmutableList; | |||
import com.google.common.collect.Lists; | |||
import java.util.ArrayList; | |||
import java.util.Arrays; | |||
import java.util.List; | |||
@@ -37,21 +35,10 @@ import org.sonar.api.batch.sensor.issue.NewIssueLocation; | |||
import org.sonar.api.rule.RuleKey; | |||
import static java.lang.String.format; | |||
import static java.util.stream.Collectors.toList; | |||
public class DefaultIssue extends DefaultStorable implements Issue, NewIssue { | |||
private static final class ToFlow implements Function<List<IssueLocation>, Flow> { | |||
@Override | |||
public Flow apply(final List<IssueLocation> input) { | |||
return new Flow() { | |||
@Override | |||
public List<IssueLocation> locations() { | |||
return ImmutableList.copyOf(input); | |||
} | |||
}; | |||
} | |||
} | |||
private RuleKey ruleKey; | |||
private Double gap; | |||
private Severity overriddenSeverity; | |||
@@ -147,7 +134,9 @@ public class DefaultIssue extends DefaultStorable implements Issue, NewIssue { | |||
@Override | |||
public List<Flow> flows() { | |||
return Lists.transform(this.flows, new ToFlow()); | |||
return this.flows.stream() | |||
.<Flow>map(l -> () -> ImmutableList.copyOf(l)) | |||
.collect(toList()); | |||
} | |||
@Override |
@@ -21,7 +21,6 @@ package org.sonar.api.batch.sensor.symbol.internal; | |||
import com.google.common.base.Preconditions; | |||
import java.util.Collection; | |||
import java.util.Comparator; | |||
import java.util.LinkedHashMap; | |||
import java.util.Map; | |||
import java.util.Set; | |||
@@ -86,12 +85,7 @@ public class DefaultSymbolTable extends DefaultStorable implements NewSymbolTabl | |||
@Override | |||
public NewSymbol newSymbol(TextRange range) { | |||
checkInputFileNotNull(); | |||
TreeSet<TextRange> references = new TreeSet<>(new Comparator<TextRange>() { | |||
@Override | |||
public int compare(TextRange o1, TextRange o2) { | |||
return o1.start().compareTo(o2.start()); | |||
} | |||
}); | |||
TreeSet<TextRange> references = new TreeSet<>((o1, o2) -> o1.start().compareTo(o2.start())); | |||
referencesBySymbol.put(range, references); | |||
return new DefaultSymbol(inputFile, range, references); | |||
} |
@@ -21,10 +21,10 @@ package org.sonar.scanner; | |||
import com.google.common.base.MoreObjects; | |||
import com.google.common.base.Preconditions; | |||
import com.google.common.base.Predicate; | |||
import com.google.common.collect.ImmutableMap; | |||
import com.google.common.collect.Maps; | |||
import java.util.Map; | |||
import java.util.Map.Entry; | |||
import org.sonar.api.batch.fs.InputFile; | |||
import org.sonar.api.batch.measure.MetricFinder; | |||
import org.sonar.api.batch.sensor.SensorContext; | |||
@@ -35,17 +35,10 @@ import org.sonar.api.utils.KeyValueFormat; | |||
import org.sonar.api.utils.KeyValueFormat.Converter; | |||
import org.sonar.scanner.scan.measure.MeasureCache; | |||
import static com.google.common.collect.Maps.filterValues; | |||
import static java.util.stream.Collectors.toMap; | |||
public class DefaultFileLinesContext implements FileLinesContext { | |||
private static final Predicate<Object> LINES_WITH_NON_ZERO_VALUE = new Predicate<Object>() { | |||
@Override | |||
public boolean apply(Object input) { | |||
return !input.equals(0); | |||
} | |||
}; | |||
private final SensorContext context; | |||
private final InputFile inputFile; | |||
private final MetricFinder metricFinder; | |||
@@ -146,7 +139,9 @@ public class DefaultFileLinesContext implements FileLinesContext { | |||
private static Map optimizeStorage(String metricKey, Map<Integer, Object> lines) { | |||
// SONAR-7464 Don't store 0 because this is default value anyway | |||
if (CoreMetrics.NCLOC_DATA_KEY.equals(metricKey) || CoreMetrics.COMMENT_LINES_DATA_KEY.equals(metricKey) || CoreMetrics.EXECUTABLE_LINES_DATA_KEY.equals(metricKey)) { | |||
return filterValues(lines, LINES_WITH_NON_ZERO_VALUE); | |||
return lines.entrySet().stream() | |||
.filter(entry -> !entry.getValue().equals(0)) | |||
.collect(toMap(Entry<Integer, Object>::getKey, Entry<Integer, Object>::getValue)); | |||
} | |||
return lines; | |||
} |
@@ -58,7 +58,7 @@ import static com.google.common.collect.FluentIterable.from; | |||
public class CpdExecutor { | |||
private static final Logger LOG = Loggers.get(CpdExecutor.class); | |||
// timeout for the computation of duplicates in a file (seconds) | |||
private static final int TIMEOUT = 5 * 60; | |||
private static final int TIMEOUT = 5 * 60 * 1000; | |||
static final int MAX_CLONE_GROUP_PER_FILE = 100; | |||
static final int MAX_CLONE_PART_PER_GROUP = 100; | |||
@@ -66,7 +66,6 @@ public class CpdExecutor { | |||
private final ReportPublisher publisher; | |||
private final BatchComponentCache batchComponentCache; | |||
private final Settings settings; | |||
private final ExecutorService executorService; | |||
private final ProgressReport progressReport; | |||
private int count; | |||
private int total; | |||
@@ -76,50 +75,54 @@ public class CpdExecutor { | |||
this.index = index; | |||
this.publisher = publisher; | |||
this.batchComponentCache = batchComponentCache; | |||
this.executorService = Executors.newSingleThreadExecutor(); | |||
this.progressReport = new ProgressReport("CPD computation", TimeUnit.SECONDS.toMillis(10)); | |||
} | |||
public void execute() { | |||
execute(TIMEOUT); | |||
} | |||
@VisibleForTesting | |||
void execute(long timeout) { | |||
total = index.noResources(); | |||
progressReport.start(String.format("Calculating CPD for %d files", total)); | |||
ExecutorService executorService = Executors.newSingleThreadExecutor(); | |||
try { | |||
Iterator<ResourceBlocks> it = index.iterator(); | |||
while (it.hasNext()) { | |||
ResourceBlocks resourceBlocks = it.next(); | |||
runCpdAnalysis(resourceBlocks.resourceId(), resourceBlocks.blocks()); | |||
runCpdAnalysis(executorService, resourceBlocks.resourceId(), resourceBlocks.blocks(), timeout); | |||
count++; | |||
} | |||
progressReport.stop("CPD calculation finished"); | |||
} catch (Exception e) { | |||
progressReport.stop(""); | |||
throw e; | |||
} finally { | |||
executorService.shutdown(); | |||
} | |||
} | |||
private void runCpdAnalysis(String resource, final Collection<Block> fileBlocks) { | |||
LOG.debug("Detection of duplications for {}", resource); | |||
BatchComponent component = batchComponentCache.get(resource); | |||
@VisibleForTesting | |||
void runCpdAnalysis(ExecutorService executorService, String componentKey, final Collection<Block> fileBlocks, long timeout) { | |||
BatchComponent component = batchComponentCache.get(componentKey); | |||
if (component == null) { | |||
LOG.error("Resource not found in component cache: {}. Skipping CPD computation for it", resource); | |||
LOG.error("Resource not found in component cache: {}. Skipping CPD computation for it", componentKey); | |||
return; | |||
} | |||
InputFile inputFile = (InputFile) component.inputComponent(); | |||
LOG.debug("Detection of duplications for {}", inputFile.absolutePath()); | |||
progressReport.message(String.format("%d/%d - current file: %s", count, total, inputFile.absolutePath())); | |||
List<CloneGroup> duplications; | |||
Future<List<CloneGroup>> futureResult = null; | |||
Future<List<CloneGroup>> futureResult = executorService.submit(() -> SuffixTreeCloneDetectionAlgorithm.detect(index, fileBlocks)); | |||
try { | |||
futureResult = executorService.submit(() -> SuffixTreeCloneDetectionAlgorithm.detect(index, fileBlocks)); | |||
duplications = futureResult.get(TIMEOUT, TimeUnit.SECONDS); | |||
duplications = futureResult.get(timeout, TimeUnit.MILLISECONDS); | |||
} catch (TimeoutException e) { | |||
LOG.warn("Timeout during detection of duplications for " + inputFile.absolutePath()); | |||
if (futureResult != null) { | |||
futureResult.cancel(true); | |||
} | |||
futureResult.cancel(true); | |||
return; | |||
} catch (Exception e) { | |||
throw new IllegalStateException("Fail during detection of duplication for " + inputFile.absolutePath(), e); | |||
@@ -158,7 +161,7 @@ public class CpdExecutor { | |||
LOG.warn("Too many duplication groups on file " + component.inputComponent() + ". Keep only the first " + MAX_CLONE_GROUP_PER_FILE + | |||
" groups."); | |||
} | |||
Iterable<org.sonar.scanner.protocol.output.ScannerReport.Duplication> reportDuplications = from(duplications) | |||
Iterable<ScannerReport.Duplication> reportDuplications = from(duplications) | |||
.limit(MAX_CLONE_GROUP_PER_FILE) | |||
.transform( | |||
new Function<CloneGroup, ScannerReport.Duplication>() { |
@@ -19,12 +19,11 @@ | |||
*/ | |||
package org.sonar.scanner.cpd.index; | |||
import com.google.common.base.Function; | |||
import com.google.common.collect.Iterables; | |||
import java.util.Collection; | |||
import java.util.HashSet; | |||
import java.util.Iterator; | |||
import java.util.Set; | |||
import java.util.stream.Collectors; | |||
import org.apache.commons.lang.StringUtils; | |||
import org.sonar.api.CoreProperties; | |||
import org.sonar.api.batch.fs.InputFile; | |||
@@ -62,18 +61,15 @@ public class SonarCpdBlockIndex extends AbstractCloneIndex { | |||
throw new UnsupportedOperationException("Trying to save CPD tokens twice for the same file is not supported: " + inputFile.absolutePath()); | |||
} | |||
final ScannerReport.CpdTextBlock.Builder builder = ScannerReport.CpdTextBlock.newBuilder(); | |||
publisher.getWriter().writeCpdTextBlocks(id, Iterables.transform(blocks, new Function<Block, ScannerReport.CpdTextBlock>() { | |||
@Override | |||
public ScannerReport.CpdTextBlock apply(Block input) { | |||
builder.clear(); | |||
builder.setStartLine(input.getStartLine()); | |||
builder.setEndLine(input.getEndLine()); | |||
builder.setStartTokenIndex(input.getStartUnit()); | |||
builder.setEndTokenIndex(input.getEndUnit()); | |||
builder.setHash(input.getBlockHash().toHexString()); | |||
return builder.build(); | |||
} | |||
})); | |||
publisher.getWriter().writeCpdTextBlocks(id, blocks.stream().map(block -> { | |||
builder.clear(); | |||
builder.setStartLine(block.getStartLine()); | |||
builder.setEndLine(block.getEndLine()); | |||
builder.setStartTokenIndex(block.getStartUnit()); | |||
builder.setEndTokenIndex(block.getEndUnit()); | |||
builder.setHash(block.getBlockHash().toHexString()); | |||
return builder.build(); | |||
}).collect(Collectors.toList())); | |||
} | |||
for (Block block : blocks) { | |||
mem.insert(block); |
@@ -166,7 +166,7 @@ public class DefaultIndex { | |||
} | |||
private static Measure toDeprecated(org.sonar.api.batch.sensor.measure.Measure<?> measure) { | |||
org.sonar.api.measures.Measure deprecatedMeasure = new org.sonar.api.measures.Measure((Metric<?>) measure.metric()); | |||
Measure deprecatedMeasure = new Measure((Metric<?>) measure.metric()); | |||
setValueAccordingToMetricType(measure, deprecatedMeasure); | |||
return deprecatedMeasure; | |||
} |
@@ -19,18 +19,13 @@ | |||
*/ | |||
package org.sonar.scanner.issue.tracking; | |||
import org.sonar.api.batch.fs.internal.FileMetadata; | |||
import org.sonar.api.batch.fs.internal.FileMetadata.LineHashConsumer; | |||
import com.google.common.collect.LinkedHashMultimap; | |||
import com.google.common.collect.Multimap; | |||
import java.util.Collection; | |||
import org.apache.commons.codec.binary.Hex; | |||
import org.apache.commons.lang.ObjectUtils; | |||
import org.sonar.api.batch.fs.internal.DefaultInputFile; | |||
import javax.annotation.Nullable; | |||
import java.util.Collection; | |||
import org.sonar.api.batch.fs.internal.FileMetadata; | |||
/** | |||
* Wraps a {@link Sequence} to assign hash codes to elements. | |||
@@ -57,13 +52,8 @@ public final class FileHashes { | |||
public static FileHashes create(DefaultInputFile f) { | |||
final byte[][] hashes = new byte[f.lines()][]; | |||
FileMetadata.computeLineHashesForIssueTracking(f, new LineHashConsumer() { | |||
@Override | |||
public void consume(int lineIdx, @Nullable byte[] hash) { | |||
hashes[lineIdx - 1] = hash; | |||
} | |||
}); | |||
FileMetadata.computeLineHashesForIssueTracking(f, | |||
(lineIdx, hash) -> hashes[lineIdx - 1] = hash); | |||
int size = hashes.length; | |||
Multimap<String, Integer> linesByHash = LinkedHashMultimap.create(); | |||
@@ -84,7 +74,7 @@ public final class FileHashes { | |||
public Collection<Integer> getLinesForHash(String hash) { | |||
return linesByHash.get(hash); | |||
} | |||
public String[] hashes() { | |||
return hashes; | |||
} |
@@ -23,7 +23,7 @@ import org.sonar.api.batch.Initializer; | |||
import org.sonar.api.batch.events.InitializerExecutionHandler; | |||
class InitializerExecutionEvent extends AbstractPhaseEvent<InitializerExecutionHandler> | |||
implements org.sonar.api.batch.events.InitializerExecutionHandler.InitializerExecutionEvent { | |||
implements InitializerExecutionHandler.InitializerExecutionEvent { | |||
private final Initializer initializer; | |||
@@ -19,13 +19,12 @@ | |||
*/ | |||
package org.sonar.scanner.phases; | |||
import java.util.List; | |||
import org.sonar.api.batch.Initializer; | |||
import org.sonar.api.batch.events.InitializersPhaseHandler; | |||
import java.util.List; | |||
class InitializersPhaseEvent extends AbstractPhaseEvent<InitializersPhaseHandler> | |||
implements org.sonar.api.batch.events.InitializersPhaseHandler.InitializersPhaseEvent { | |||
implements InitializersPhaseHandler.InitializersPhaseEvent { | |||
private final List<Initializer> initializers; | |||
@@ -23,7 +23,7 @@ import org.sonar.api.batch.PostJob; | |||
import org.sonar.api.batch.events.PostJobExecutionHandler; | |||
class PostJobExecutionEvent extends AbstractPhaseEvent<PostJobExecutionHandler> | |||
implements org.sonar.api.batch.events.PostJobExecutionHandler.PostJobExecutionEvent { | |||
implements PostJobExecutionHandler.PostJobExecutionEvent { | |||
private final PostJob postJob; | |||
@@ -19,13 +19,12 @@ | |||
*/ | |||
package org.sonar.scanner.phases; | |||
import java.util.List; | |||
import org.sonar.api.batch.PostJob; | |||
import org.sonar.api.batch.events.PostJobsPhaseHandler; | |||
import java.util.List; | |||
class PostJobPhaseEvent extends AbstractPhaseEvent<PostJobsPhaseHandler> | |||
implements org.sonar.api.batch.events.PostJobsPhaseHandler.PostJobsPhaseEvent { | |||
implements PostJobsPhaseHandler.PostJobsPhaseEvent { | |||
private final List<PostJob> postJobs; | |||
@@ -23,7 +23,7 @@ import org.sonar.api.batch.events.ProjectAnalysisHandler; | |||
import org.sonar.api.resources.Project; | |||
class ProjectAnalysisEvent extends AbstractPhaseEvent<ProjectAnalysisHandler> | |||
implements org.sonar.api.batch.events.ProjectAnalysisHandler.ProjectAnalysisEvent { | |||
implements ProjectAnalysisHandler.ProjectAnalysisEvent { | |||
private final Project project; | |||
@@ -23,7 +23,7 @@ import org.sonar.api.batch.Sensor; | |||
import org.sonar.api.batch.events.SensorExecutionHandler; | |||
class SensorExecutionEvent extends AbstractPhaseEvent<SensorExecutionHandler> | |||
implements org.sonar.api.batch.events.SensorExecutionHandler.SensorExecutionEvent { | |||
implements SensorExecutionHandler.SensorExecutionEvent { | |||
private final Sensor sensor; | |||
@@ -22,7 +22,6 @@ package org.sonar.scanner.profiling; | |||
import java.util.ArrayList; | |||
import java.util.Collection; | |||
import java.util.Collections; | |||
import java.util.Comparator; | |||
import java.util.LinkedHashMap; | |||
import java.util.List; | |||
import java.util.Map; | |||
@@ -72,14 +71,8 @@ public abstract class AbstractTimeProfiling { | |||
} | |||
static <G extends AbstractTimeProfiling> Map<Object, G> sortByDescendingTotalTime(Map<?, G> unsorted) { | |||
List<Map.Entry<?, G>> entries = | |||
new ArrayList<Map.Entry<?, G>>(unsorted.entrySet()); | |||
Collections.sort(entries, new Comparator<Map.Entry<?, G>>() { | |||
@Override | |||
public int compare(Map.Entry<?, G> o1, Map.Entry<?, G> o2) { | |||
return Long.valueOf(o2.getValue().totalTime()).compareTo(o1.getValue().totalTime()); | |||
} | |||
}); | |||
List<Map.Entry<?, G>> entries = new ArrayList<>(unsorted.entrySet()); | |||
Collections.sort(entries, (o1, o2) -> Long.valueOf(o2.getValue().totalTime()).compareTo(o1.getValue().totalTime())); | |||
Map<Object, G> sortedMap = new LinkedHashMap<>(); | |||
for (Map.Entry<?, G> entry : entries) { | |||
sortedMap.put(entry.getKey(), entry.getValue()); |
@@ -19,15 +19,13 @@ | |||
*/ | |||
package org.sonar.scanner.report; | |||
import com.google.common.base.Function; | |||
import com.google.common.collect.FluentIterable; | |||
import javax.annotation.Nonnull; | |||
import org.sonar.api.batch.rule.ActiveRule; | |||
import org.sonar.api.batch.rule.ActiveRules; | |||
import org.sonar.scanner.protocol.Constants; | |||
import org.sonar.scanner.protocol.output.ScannerReport; | |||
import org.sonar.scanner.protocol.output.ScannerReportWriter; | |||
import static java.util.stream.Collectors.toList; | |||
public class ActiveRulesPublisher implements ReportPublisherStep { | |||
private final ActiveRules activeRules; | |||
@@ -38,21 +36,16 @@ public class ActiveRulesPublisher implements ReportPublisherStep { | |||
@Override | |||
public void publish(ScannerReportWriter writer) { | |||
Iterable<ScannerReport.ActiveRule> activeRuleMessages = FluentIterable.from(activeRules.findAll()).transform(new ToMessage()); | |||
writer.writeActiveRules(activeRuleMessages); | |||
final ScannerReport.ActiveRule.Builder builder = ScannerReport.ActiveRule.newBuilder(); | |||
writer.writeActiveRules(activeRules.findAll().stream() | |||
.map(input -> { | |||
builder.clear(); | |||
builder.setRuleRepository(input.ruleKey().repository()); | |||
builder.setRuleKey(input.ruleKey().rule()); | |||
builder.setSeverity(Constants.Severity.valueOf(input.severity())); | |||
builder.getMutableParamsByKey().putAll(input.params()); | |||
return builder.build(); | |||
}).collect(toList())); | |||
} | |||
private static class ToMessage implements Function<ActiveRule, ScannerReport.ActiveRule> { | |||
private final ScannerReport.ActiveRule.Builder builder = ScannerReport.ActiveRule.newBuilder(); | |||
@Override | |||
public ScannerReport.ActiveRule apply(@Nonnull ActiveRule input) { | |||
builder.clear(); | |||
builder.setRuleRepository(input.ruleKey().repository()); | |||
builder.setRuleKey(input.ruleKey().rule()); | |||
builder.setSeverity(Constants.Severity.valueOf(input.severity())); | |||
builder.getMutableParamsByKey().putAll(input.params()); | |||
return builder.build(); | |||
} | |||
} | |||
} |
@@ -19,13 +19,11 @@ | |||
*/ | |||
package org.sonar.scanner.report; | |||
import com.google.common.base.Function; | |||
import com.google.common.collect.Iterables; | |||
import java.io.Serializable; | |||
import java.util.Collections; | |||
import java.util.Map; | |||
import java.util.stream.StreamSupport; | |||
import javax.annotation.Nonnull; | |||
import org.sonar.api.batch.fs.InputFile; | |||
import org.sonar.api.batch.fs.InputFile.Type; | |||
import org.sonar.api.batch.measure.Metric; | |||
@@ -34,6 +32,7 @@ import org.sonar.api.measures.CoreMetrics; | |||
import org.sonar.api.test.MutableTestPlan; | |||
import org.sonar.api.test.TestCase.Status; | |||
import org.sonar.api.utils.KeyValueFormat; | |||
import org.sonar.core.util.stream.Collectors; | |||
import org.sonar.scanner.deprecated.test.TestPlanBuilder; | |||
import org.sonar.scanner.index.BatchComponent; | |||
import org.sonar.scanner.index.BatchComponentCache; | |||
@@ -46,7 +45,6 @@ import org.sonar.scanner.protocol.output.ScannerReport.Measure.StringValue; | |||
import org.sonar.scanner.protocol.output.ScannerReportWriter; | |||
import org.sonar.scanner.scan.measure.MeasureCache; | |||
import static com.google.common.collect.Iterables.transform; | |||
import static org.sonar.api.measures.CoreMetrics.CONDITIONS_TO_COVER; | |||
import static org.sonar.api.measures.CoreMetrics.CONDITIONS_TO_COVER_KEY; | |||
import static org.sonar.api.measures.CoreMetrics.LINES_TO_COVER; | |||
@@ -68,53 +66,6 @@ import static org.sonar.api.measures.CoreMetrics.UNCOVERED_LINES_KEY; | |||
public class MeasuresPublisher implements ReportPublisherStep { | |||
private static final class MeasureToReportMeasure implements Function<DefaultMeasure, ScannerReport.Measure> { | |||
private final BatchComponent resource; | |||
private final ScannerReport.Measure.Builder builder = ScannerReport.Measure.newBuilder(); | |||
private MeasureToReportMeasure(BatchComponent resource) { | |||
this.resource = resource; | |||
} | |||
@Override | |||
public ScannerReport.Measure apply(@Nonnull DefaultMeasure input) { | |||
validateMeasure(input, resource.key()); | |||
return toReportMeasure(builder, input); | |||
} | |||
private static void validateMeasure(DefaultMeasure measure, String componentKey) { | |||
if (measure.value() == null) { | |||
throw new IllegalArgumentException(String.format("Measure on metric '%s' and component '%s' has no value, but it's not allowed", measure.metric().key(), componentKey)); | |||
} | |||
} | |||
private static ScannerReport.Measure toReportMeasure(ScannerReport.Measure.Builder builder, DefaultMeasure measure) { | |||
builder.clear(); | |||
builder.setMetricKey(measure.metric().key()); | |||
setValueAccordingToType(builder, measure); | |||
return builder.build(); | |||
} | |||
private static void setValueAccordingToType(ScannerReport.Measure.Builder builder, DefaultMeasure measure) { | |||
Serializable value = measure.value(); | |||
Metric<?> metric = measure.metric(); | |||
if (Boolean.class.equals(metric.valueType())) { | |||
builder.setBooleanValue(BoolValue.newBuilder().setValue(((Boolean) value).booleanValue())); | |||
} else if (Integer.class.equals(metric.valueType())) { | |||
builder.setIntValue(IntValue.newBuilder().setValue(((Number) value).intValue())); | |||
} else if (Double.class.equals(metric.valueType())) { | |||
builder.setDoubleValue(DoubleValue.newBuilder().setValue(((Number) value).doubleValue())); | |||
} else if (String.class.equals(metric.valueType())) { | |||
builder.setStringValue(StringValue.newBuilder().setValue((String) value)); | |||
} else if (Long.class.equals(metric.valueType())) { | |||
builder.setLongValue(LongValue.newBuilder().setValue(((Number) value).longValue())); | |||
} else { | |||
throw new UnsupportedOperationException("Unsupported type :" + metric.valueType()); | |||
} | |||
} | |||
} | |||
private final BatchComponentCache componentCache; | |||
private final MeasureCache measureCache; | |||
private final TestPlanBuilder testPlanBuilder; | |||
@@ -127,6 +78,8 @@ public class MeasuresPublisher implements ReportPublisherStep { | |||
@Override | |||
public void publish(ScannerReportWriter writer) { | |||
final ScannerReport.Measure.Builder builder = ScannerReport.Measure.newBuilder(); | |||
for (final BatchComponent component : componentCache.all()) { | |||
// Recompute all coverage measures from line data to take into account the possible merge of several reports | |||
updateCoverageFromLineData(component); | |||
@@ -134,8 +87,35 @@ public class MeasuresPublisher implements ReportPublisherStep { | |||
updateTestExecutionFromTestPlan(component); | |||
Iterable<DefaultMeasure<?>> scannerMeasures = measureCache.byComponentKey(component.key()); | |||
Iterable<ScannerReport.Measure> reportMeasures = transform(scannerMeasures, new MeasureToReportMeasure(component)); | |||
writer.writeComponentMeasures(component.batchId(), reportMeasures); | |||
writer.writeComponentMeasures(component.batchId(), StreamSupport.stream(scannerMeasures.spliterator(), false) | |||
.map(input -> { | |||
if (input.value() == null) { | |||
throw new IllegalArgumentException( | |||
String.format("Measure on metric '%s' and component '%s' has no value, but it's not allowed", input.metric().key(), component.key())); | |||
} | |||
builder.clear(); | |||
builder.setMetricKey(input.metric().key()); | |||
setValueAccordingToType(builder, input); | |||
return builder.build(); | |||
}).collect(Collectors.toList())); | |||
} | |||
} | |||
private static void setValueAccordingToType(ScannerReport.Measure.Builder builder, DefaultMeasure measure) { | |||
Serializable value = measure.value(); | |||
Metric<?> metric = measure.metric(); | |||
if (Boolean.class.equals(metric.valueType())) { | |||
builder.setBooleanValue(BoolValue.newBuilder().setValue(((Boolean) value).booleanValue())); | |||
} else if (Integer.class.equals(metric.valueType())) { | |||
builder.setIntValue(IntValue.newBuilder().setValue(((Number) value).intValue())); | |||
} else if (Double.class.equals(metric.valueType())) { | |||
builder.setDoubleValue(DoubleValue.newBuilder().setValue(((Number) value).doubleValue())); | |||
} else if (String.class.equals(metric.valueType())) { | |||
builder.setStringValue(StringValue.newBuilder().setValue((String) value)); | |||
} else if (Long.class.equals(metric.valueType())) { | |||
builder.setLongValue(LongValue.newBuilder().setValue(((Number) value).longValue())); | |||
} else { | |||
throw new UnsupportedOperationException("Unsupported type :" + metric.valueType()); | |||
} | |||
} | |||
@@ -19,11 +19,10 @@ | |||
*/ | |||
package org.sonar.scanner.report; | |||
import com.google.common.base.Function; | |||
import com.google.common.collect.Iterables; | |||
import java.util.HashSet; | |||
import java.util.Set; | |||
import javax.annotation.Nonnull; | |||
import java.util.stream.StreamSupport; | |||
import org.sonar.api.test.CoverageBlock; | |||
import org.sonar.api.test.MutableTestCase; | |||
import org.sonar.api.test.MutableTestPlan; | |||
@@ -38,69 +37,9 @@ import org.sonar.scanner.protocol.output.ScannerReport.Test; | |||
import org.sonar.scanner.protocol.output.ScannerReport.Test.TestStatus; | |||
import org.sonar.scanner.protocol.output.ScannerReportWriter; | |||
public class TestExecutionAndCoveragePublisher implements ReportPublisherStep { | |||
private static final class TestConverter implements Function<MutableTestCase, ScannerReport.Test> { | |||
private final Set<String> testNamesWithCoverage; | |||
private ScannerReport.Test.Builder builder = ScannerReport.Test.newBuilder(); | |||
private TestConverter(Set<String> testNamesWithCoverage) { | |||
this.testNamesWithCoverage = testNamesWithCoverage; | |||
} | |||
@Override | |||
public Test apply(@Nonnull MutableTestCase testCase) { | |||
builder.clear(); | |||
builder.setName(testCase.name()); | |||
if (testCase.doesCover()) { | |||
testNamesWithCoverage.add(testCase.name()); | |||
} | |||
Long durationInMs = testCase.durationInMs(); | |||
if (durationInMs != null) { | |||
builder.setDurationInMs(durationInMs.longValue()); | |||
} | |||
String msg = testCase.message(); | |||
if (msg != null) { | |||
builder.setMsg(msg); | |||
} | |||
String stack = testCase.stackTrace(); | |||
if (stack != null) { | |||
builder.setStacktrace(stack); | |||
} | |||
TestCase.Status status = testCase.status(); | |||
if (status != null) { | |||
builder.setStatus(TestStatus.valueOf(status.name())); | |||
} | |||
return builder.build(); | |||
} | |||
} | |||
private final class TestCoverageConverter implements Function<String, CoverageDetail> { | |||
private final MutableTestPlan testPlan; | |||
private ScannerReport.CoverageDetail.Builder builder = ScannerReport.CoverageDetail.newBuilder(); | |||
private ScannerReport.CoverageDetail.CoveredFile.Builder coveredBuilder = ScannerReport.CoverageDetail.CoveredFile.newBuilder(); | |||
private TestCoverageConverter(MutableTestPlan testPlan) { | |||
this.testPlan = testPlan; | |||
} | |||
import static java.util.stream.Collectors.toList; | |||
@Override | |||
public CoverageDetail apply(@Nonnull String testName) { | |||
// Take first test with provided name | |||
MutableTestCase testCase = testPlan.testCasesByName(testName).iterator().next(); | |||
builder.clear(); | |||
builder.setTestName(testName); | |||
for (CoverageBlock block : testCase.coverageBlocks()) { | |||
coveredBuilder.clear(); | |||
coveredBuilder.setFileRef(componentCache.get(((DefaultTestable) block.testable()).inputFile().key()).batchId()); | |||
for (int line : block.lines()) { | |||
coveredBuilder.addCoveredLine(line); | |||
} | |||
builder.addCoveredFile(coveredBuilder.build()); | |||
} | |||
return builder.build(); | |||
} | |||
} | |||
public class TestExecutionAndCoveragePublisher implements ReportPublisherStep { | |||
private final BatchComponentCache componentCache; | |||
private final TestPlanBuilder testPlanBuilder; | |||
@@ -112,6 +51,9 @@ public class TestExecutionAndCoveragePublisher implements ReportPublisherStep { | |||
@Override | |||
public void publish(ScannerReportWriter writer) { | |||
final ScannerReport.Test.Builder testBuilder = ScannerReport.Test.newBuilder(); | |||
final ScannerReport.CoverageDetail.Builder builder = ScannerReport.CoverageDetail.newBuilder(); | |||
final ScannerReport.CoverageDetail.CoveredFile.Builder coveredBuilder = ScannerReport.CoverageDetail.CoveredFile.newBuilder(); | |||
for (final BatchComponent component : componentCache.all()) { | |||
final MutableTestPlan testPlan = testPlanBuilder.loadPerspective(MutableTestPlan.class, component.inputComponent()); | |||
if (testPlan == null || Iterables.isEmpty(testPlan.testCases())) { | |||
@@ -120,9 +62,56 @@ public class TestExecutionAndCoveragePublisher implements ReportPublisherStep { | |||
final Set<String> testNamesWithCoverage = new HashSet<>(); | |||
writer.writeTests(component.batchId(), Iterables.transform(testPlan.testCases(), new TestConverter(testNamesWithCoverage))); | |||
writer.writeTests(component.batchId(), | |||
StreamSupport.stream(testPlan.testCases().spliterator(), false) | |||
.map(testCase -> toProtobufTest(testBuilder, testNamesWithCoverage, testCase)) | |||
.collect(toList())); | |||
writer.writeCoverageDetails(component.batchId(), Iterables.transform(testNamesWithCoverage, new TestCoverageConverter(testPlan))); | |||
writer.writeCoverageDetails(component.batchId(), testNamesWithCoverage.stream() | |||
.map(testName -> toProtobufCoverageDetails(builder, coveredBuilder, testPlan, testName)) | |||
.collect(toList())); | |||
} | |||
} | |||
private CoverageDetail toProtobufCoverageDetails(final ScannerReport.CoverageDetail.Builder builder, final ScannerReport.CoverageDetail.CoveredFile.Builder coveredBuilder, | |||
final MutableTestPlan testPlan, String testName) { | |||
// Take first test with provided name | |||
MutableTestCase testCase = testPlan.testCasesByName(testName).iterator().next(); | |||
builder.clear(); | |||
builder.setTestName(testName); | |||
for (CoverageBlock block : testCase.coverageBlocks()) { | |||
coveredBuilder.clear(); | |||
coveredBuilder.setFileRef(componentCache.get(((DefaultTestable) block.testable()).inputFile().key()).batchId()); | |||
for (int line : block.lines()) { | |||
coveredBuilder.addCoveredLine(line); | |||
} | |||
builder.addCoveredFile(coveredBuilder.build()); | |||
} | |||
return builder.build(); | |||
} | |||
private static Test toProtobufTest(final ScannerReport.Test.Builder testBuilder, final Set<String> testNamesWithCoverage, MutableTestCase testCase) { | |||
testBuilder.clear(); | |||
testBuilder.setName(testCase.name()); | |||
if (testCase.doesCover()) { | |||
testNamesWithCoverage.add(testCase.name()); | |||
} | |||
Long durationInMs = testCase.durationInMs(); | |||
if (durationInMs != null) { | |||
testBuilder.setDurationInMs(durationInMs.longValue()); | |||
} | |||
String msg = testCase.message(); | |||
if (msg != null) { | |||
testBuilder.setMsg(msg); | |||
} | |||
String stack = testCase.stackTrace(); | |||
if (stack != null) { | |||
testBuilder.setStacktrace(stack); | |||
} | |||
TestCase.Status status = testCase.status(); | |||
if (status != null) { | |||
testBuilder.setStatus(TestStatus.valueOf(status.name())); | |||
} | |||
return testBuilder.build(); | |||
} | |||
} |
@@ -20,22 +20,20 @@ | |||
package org.sonar.scanner.repository; | |||
import com.google.common.base.Throwables; | |||
import com.google.common.collect.HashBasedTable; | |||
import com.google.common.collect.Table; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.net.HttpURLConnection; | |||
import java.util.Date; | |||
import java.util.Map; | |||
import org.apache.commons.io.IOUtils; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
import org.sonar.api.utils.MessageException; | |||
import org.sonar.scanner.bootstrap.BatchWsClient; | |||
import org.sonar.scanner.util.BatchUtils; | |||
import org.sonarqube.ws.WsBatch; | |||
import org.sonarqube.ws.WsBatch.WsProjectResponse; | |||
import org.sonarqube.ws.WsBatch.WsProjectResponse.FileDataByPath; | |||
import org.sonarqube.ws.WsBatch.WsProjectResponse.Settings; | |||
@@ -109,7 +107,7 @@ public class DefaultProjectRepositoriesLoader implements ProjectRepositoriesLoad | |||
Map<String, FileDataByPath> fileDataByModuleAndPath = response.getFileDataByModuleAndPath(); | |||
for (Map.Entry<String, FileDataByPath> e1 : fileDataByModuleAndPath.entrySet()) { | |||
for (Map.Entry<String, org.sonarqube.ws.WsBatch.WsProjectResponse.FileData> e2 : e1.getValue().getFileDataByPath().entrySet()) { | |||
for (Map.Entry<String, WsBatch.WsProjectResponse.FileData> e2 : e1.getValue().getFileDataByPath().entrySet()) { | |||
FileData fd = new FileData(e2.getValue().getHash(), e2.getValue().getRevision()); | |||
fileDataTable.put(e1.getKey(), e2.getKey(), fd); | |||
} |
@@ -25,17 +25,17 @@ import java.util.HashMap; | |||
import java.util.LinkedList; | |||
import java.util.List; | |||
import java.util.Map; | |||
import org.apache.commons.io.IOUtils; | |||
import org.sonar.api.rule.RuleKey; | |||
import org.sonar.scanner.bootstrap.BatchWsClient; | |||
import org.sonar.scanner.util.BatchUtils; | |||
import org.sonarqube.ws.Rules; | |||
import org.sonarqube.ws.Rules.Active; | |||
import org.sonarqube.ws.Rules.Active.Param; | |||
import org.sonarqube.ws.client.GetRequest; | |||
import org.sonarqube.ws.Rules.ActiveList; | |||
import org.sonarqube.ws.Rules.Rule; | |||
import org.sonarqube.ws.Rules.SearchResponse; | |||
import org.sonarqube.ws.client.GetRequest; | |||
public class DefaultActiveRulesLoader implements ActiveRulesLoader { | |||
private static final String RULES_SEARCH_URL = "/api/rules/search.protobuf?f=repo,name,severity,lang,internalKey,templateKey,params,actives&activation=true"; | |||
@@ -112,7 +112,7 @@ public class DefaultActiveRulesLoader implements ActiveRulesLoader { | |||
Map<String, String> params = new HashMap<>(); | |||
for (org.sonarqube.ws.Rules.Rule.Param param : r.getParams().getParamsList()) { | |||
for (Rules.Rule.Param param : r.getParams().getParamsList()) { | |||
params.put(param.getKey(), param.getDefaultValue()); | |||
} | |||
@@ -35,7 +35,6 @@ import java.util.Collections; | |||
import java.util.HashSet; | |||
import java.util.List; | |||
import java.util.Set; | |||
import java.util.concurrent.Callable; | |||
import java.util.concurrent.ExecutionException; | |||
import java.util.concurrent.ExecutorService; | |||
import java.util.concurrent.Executors; | |||
@@ -160,22 +159,19 @@ public class FileIndexer { | |||
private void indexFile(final InputFileBuilder inputFileBuilder, final DefaultModuleFileSystem fs, | |||
final Progress status, final DefaultInputFile inputFile, final InputFile.Type type) { | |||
tasks.add(executorService.submit(new Callable<Void>() { | |||
@Override | |||
public Void call() { | |||
DefaultInputFile completedInputFile = inputFileBuilder.completeAndComputeMetadata(inputFile, type); | |||
if (completedInputFile != null && accept(completedInputFile)) { | |||
fs.add(completedInputFile); | |||
status.markAsIndexed(completedInputFile); | |||
File parentDir = completedInputFile.file().getParentFile(); | |||
String relativePath = new PathResolver().relativePath(fs.baseDir(), parentDir); | |||
if (relativePath != null) { | |||
DefaultInputDir inputDir = new DefaultInputDir(fs.moduleKey(), relativePath); | |||
fs.add(inputDir); | |||
} | |||
tasks.add(executorService.submit(() -> { | |||
DefaultInputFile completedInputFile = inputFileBuilder.completeAndComputeMetadata(inputFile, type); | |||
if (completedInputFile != null && accept(completedInputFile)) { | |||
fs.add(completedInputFile); | |||
status.markAsIndexed(completedInputFile); | |||
File parentDir = completedInputFile.file().getParentFile(); | |||
String relativePath = new PathResolver().relativePath(fs.baseDir(), parentDir); | |||
if (relativePath != null) { | |||
DefaultInputDir inputDir = new DefaultInputDir(fs.moduleKey(), relativePath); | |||
fs.add(inputDir); | |||
} | |||
return null; | |||
} | |||
return null; | |||
})); | |||
} |
@@ -21,13 +21,13 @@ package org.sonar.scanner.scan.measure; | |||
import com.google.common.collect.Lists; | |||
import com.google.common.collect.Maps; | |||
import org.sonar.api.measures.Metric; | |||
import org.sonar.api.measures.Metric.ValueType; | |||
import org.sonar.scanner.protocol.input.GlobalRepositories; | |||
import org.sonar.api.measures.MetricFinder; | |||
import java.util.Collection; | |||
import java.util.List; | |||
import java.util.Map; | |||
import org.sonar.api.measures.Metric; | |||
import org.sonar.api.measures.Metric.ValueType; | |||
import org.sonar.api.measures.MetricFinder; | |||
import org.sonar.scanner.protocol.input.GlobalRepositories; | |||
public final class DeprecatedMetricFinder implements MetricFinder { | |||
@@ -36,7 +36,7 @@ public final class DeprecatedMetricFinder implements MetricFinder { | |||
public DeprecatedMetricFinder(GlobalRepositories globalReferentials) { | |||
for (org.sonar.scanner.protocol.input.Metric metric : globalReferentials.metrics()) { | |||
Metric hibernateMetric = new org.sonar.api.measures.Metric.Builder(metric.key(), metric.name(), ValueType.valueOf(metric.valueType())) | |||
Metric hibernateMetric = new Metric.Builder(metric.key(), metric.name(), ValueType.valueOf(metric.valueType())) | |||
.create() | |||
.setDirection(metric.direction()) | |||
.setQualitative(metric.isQualitative()) | |||
@@ -47,7 +47,7 @@ public final class DeprecatedMetricFinder implements MetricFinder { | |||
.setWorstValue(metric.worstValue()) | |||
.setId(metric.id()); | |||
metricsByKey.put(metric.key(), hibernateMetric); | |||
metricsById.put(metric.id(), new org.sonar.api.measures.Metric.Builder(metric.key(), metric.key(), ValueType.valueOf(metric.valueType())).create().setId(metric.id())); | |||
metricsById.put(metric.id(), new Metric.Builder(metric.key(), metric.key(), ValueType.valueOf(metric.valueType())).create().setId(metric.id())); | |||
} | |||
} | |||
@@ -20,8 +20,6 @@ | |||
package org.sonar.scanner.sensor; | |||
import com.google.common.annotations.VisibleForTesting; | |||
import com.google.common.base.Function; | |||
import com.google.common.collect.Iterables; | |||
import java.util.Arrays; | |||
import java.util.Collection; | |||
import java.util.HashMap; | |||
@@ -32,7 +30,6 @@ import java.util.Set; | |||
import java.util.TreeMap; | |||
import java.util.stream.Collectors; | |||
import java.util.stream.Stream; | |||
import javax.annotation.Nonnull; | |||
import org.sonar.api.batch.fs.InputComponent; | |||
import org.sonar.api.batch.fs.InputFile; | |||
import org.sonar.api.batch.fs.TextRange; | |||
@@ -43,7 +40,6 @@ import org.sonar.api.batch.sensor.coverage.internal.DefaultCoverage; | |||
import org.sonar.api.batch.sensor.cpd.internal.DefaultCpdTokens; | |||
import org.sonar.api.batch.sensor.error.AnalysisError; | |||
import org.sonar.api.batch.sensor.highlighting.internal.DefaultHighlighting; | |||
import org.sonar.api.batch.sensor.highlighting.internal.SyntaxHighlightingRule; | |||
import org.sonar.api.batch.sensor.internal.SensorStorage; | |||
import org.sonar.api.batch.sensor.issue.Issue; | |||
import org.sonar.api.batch.sensor.measure.Measure; | |||
@@ -70,6 +66,7 @@ import org.sonar.scanner.repository.ContextPropertiesCache; | |||
import org.sonar.scanner.scan.measure.MeasureCache; | |||
import org.sonar.scanner.sensor.coverage.CoverageExclusions; | |||
import static java.util.stream.Collectors.toList; | |||
import static org.sonar.api.measures.CoreMetrics.BRANCH_COVERAGE; | |||
import static org.sonar.api.measures.CoreMetrics.COMMENTED_OUT_CODE_LINES_KEY; | |||
import static org.sonar.api.measures.CoreMetrics.CONDITIONS_BY_LINE; | |||
@@ -361,8 +358,20 @@ public class DefaultSensorStorage implements SensorStorage { | |||
if (writer.hasComponentData(FileStructure.Domain.SYNTAX_HIGHLIGHTINGS, componentRef)) { | |||
throw new UnsupportedOperationException("Trying to save highlighting twice for the same file is not supported: " + inputFile.absolutePath()); | |||
} | |||
final ScannerReport.SyntaxHighlightingRule.Builder builder = ScannerReport.SyntaxHighlightingRule.newBuilder(); | |||
final ScannerReport.TextRange.Builder rangeBuilder = ScannerReport.TextRange.newBuilder(); | |||
writer.writeComponentSyntaxHighlighting(componentRef, | |||
Iterables.transform(highlighting.getSyntaxHighlightingRuleSet(), new BuildSyntaxHighlighting())); | |||
highlighting.getSyntaxHighlightingRuleSet().stream() | |||
.map(input -> { | |||
builder.setRange(rangeBuilder.setStartLine(input.range().start().line()) | |||
.setStartOffset(input.range().start().lineOffset()) | |||
.setEndLine(input.range().end().line()) | |||
.setEndOffset(input.range().end().lineOffset()) | |||
.build()); | |||
builder.setType(ScannerReportUtils.toProtocolType(input.getTextType())); | |||
return builder.build(); | |||
}).collect(toList())); | |||
} | |||
@Override | |||
@@ -372,13 +381,11 @@ public class DefaultSensorStorage implements SensorStorage { | |||
if (writer.hasComponentData(FileStructure.Domain.SYMBOLS, componentRef)) { | |||
throw new UnsupportedOperationException("Trying to save symbol table twice for the same file is not supported: " + symbolTable.inputFile().absolutePath()); | |||
} | |||
final ScannerReport.Symbol.Builder builder = ScannerReport.Symbol.newBuilder(); | |||
final ScannerReport.TextRange.Builder rangeBuilder = ScannerReport.TextRange.newBuilder(); | |||
writer.writeComponentSymbols(componentRef, | |||
Iterables.transform(symbolTable.getReferencesBySymbol().entrySet(), new Function<Map.Entry<TextRange, Set<TextRange>>, ScannerReport.Symbol>() { | |||
private ScannerReport.Symbol.Builder builder = ScannerReport.Symbol.newBuilder(); | |||
private ScannerReport.TextRange.Builder rangeBuilder = ScannerReport.TextRange.newBuilder(); | |||
@Override | |||
public ScannerReport.Symbol apply(Map.Entry<TextRange, Set<TextRange>> input) { | |||
symbolTable.getReferencesBySymbol().entrySet().stream() | |||
.map(input -> { | |||
builder.clear(); | |||
rangeBuilder.clear(); | |||
TextRange declaration = input.getKey(); | |||
@@ -395,9 +402,7 @@ public class DefaultSensorStorage implements SensorStorage { | |||
.build()); | |||
} | |||
return builder.build(); | |||
} | |||
})); | |||
}).collect(Collectors.toList())); | |||
} | |||
@Override | |||
@@ -424,22 +429,6 @@ public class DefaultSensorStorage implements SensorStorage { | |||
} | |||
} | |||
private static class BuildSyntaxHighlighting implements Function<SyntaxHighlightingRule, ScannerReport.SyntaxHighlightingRule> { | |||
private ScannerReport.SyntaxHighlightingRule.Builder builder = ScannerReport.SyntaxHighlightingRule.newBuilder(); | |||
private ScannerReport.TextRange.Builder rangeBuilder = ScannerReport.TextRange.newBuilder(); | |||
@Override | |||
public ScannerReport.SyntaxHighlightingRule apply(@Nonnull SyntaxHighlightingRule input) { | |||
builder.setRange(rangeBuilder.setStartLine(input.range().start().line()) | |||
.setStartOffset(input.range().start().lineOffset()) | |||
.setEndLine(input.range().end().line()) | |||
.setEndOffset(input.range().end().lineOffset()) | |||
.build()); | |||
builder.setType(ScannerReportUtils.toProtocolType(input.getTextType())); | |||
return builder.build(); | |||
} | |||
} | |||
@Override | |||
public void store(DefaultCpdTokens defaultCpdTokens) { | |||
InputFile inputFile = defaultCpdTokens.inputFile(); |
@@ -81,14 +81,14 @@ public class DefaultHighlightable implements Highlightable { | |||
@Override | |||
public HighlightingBuilder highlight(int startOffset, int endOffset, String typeOfText) { | |||
TypeOfText type = org.sonar.api.batch.sensor.highlighting.TypeOfText.forCssClass(typeOfText); | |||
TypeOfText type = TypeOfText.forCssClass(typeOfText); | |||
defaultHighlighting.highlight(startOffset, endOffset, type); | |||
return this; | |||
} | |||
@Override | |||
public HighlightingBuilder highlight(int startLine, int startLineOffset, int endLine, int endLineOffset, String typeOfText) { | |||
TypeOfText type = org.sonar.api.batch.sensor.highlighting.TypeOfText.forCssClass(typeOfText); | |||
TypeOfText type = TypeOfText.forCssClass(typeOfText); | |||
defaultHighlighting.highlight(startLine, startLineOffset, endLine, endLineOffset, type); | |||
return this; | |||
} |
@@ -28,15 +28,19 @@ import java.util.List; | |||
import org.junit.Before; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.junit.rules.ExpectedException; | |||
import org.junit.rules.TemporaryFolder; | |||
import org.sonar.api.batch.fs.InputFile; | |||
import org.sonar.api.batch.fs.internal.DefaultInputFile; | |||
import org.sonar.api.batch.fs.internal.DefaultInputModule; | |||
import org.sonar.api.config.Settings; | |||
import org.sonar.api.config.MapSettings; | |||
import org.sonar.api.config.Settings; | |||
import org.sonar.api.resources.Project; | |||
import org.sonar.api.utils.log.LogTester; | |||
import org.sonar.api.utils.log.LoggerLevel; | |||
import org.sonar.core.util.CloseableIterator; | |||
import org.sonar.duplications.block.Block; | |||
import org.sonar.duplications.block.ByteArray; | |||
import org.sonar.duplications.index.CloneGroup; | |||
import org.sonar.duplications.index.ClonePart; | |||
import org.sonar.scanner.cpd.index.SonarCpdBlockIndex; | |||
@@ -65,22 +69,25 @@ public class CpdExecutorTest { | |||
@Rule | |||
public TemporaryFolder temp = new TemporaryFolder(); | |||
// private AbstractCpdEngine engine; | |||
@Rule | |||
public ExpectedException thrown = ExpectedException.none(); | |||
private ScannerReportReader reader; | |||
private BatchComponent batchComponent1; | |||
private BatchComponent batchComponent2; | |||
private BatchComponent batchComponent3; | |||
private File baseDir; | |||
@Before | |||
public void setUp() throws IOException { | |||
File outputDir = temp.newFolder(); | |||
baseDir = temp.newFolder(); | |||
settings = new MapSettings(); | |||
index = mock(SonarCpdBlockIndex.class); | |||
publisher = mock(ReportPublisher.class); | |||
when(publisher.getWriter()).thenReturn(new ScannerReportWriter(outputDir)); | |||
componentCache = new BatchComponentCache(); | |||
index = new SonarCpdBlockIndex(publisher, componentCache, settings); | |||
executor = new CpdExecutor(settings, index, publisher, componentCache); | |||
reader = new ScannerReportReader(outputDir); | |||
@@ -94,7 +101,10 @@ public class CpdExecutorTest { | |||
private BatchComponent createComponent(String relativePath, int lines) { | |||
org.sonar.api.resources.Resource sampleFile = org.sonar.api.resources.File.create("relativePath").setEffectiveKey("foo:" + relativePath); | |||
return componentCache.add(sampleFile, null).setInputComponent(new DefaultInputFile("foo", relativePath).setLines(lines)); | |||
return componentCache.add(sampleFile, null) | |||
.setInputComponent(new DefaultInputFile("foo", relativePath) | |||
.setModuleBaseDir(baseDir.toPath()) | |||
.setLines(lines)); | |||
} | |||
@Test | |||
@@ -197,6 +207,39 @@ public class CpdExecutorTest { | |||
assertDuplication(dups[1], 15, 214, batchComponent3.batchId(), 15, 214); | |||
} | |||
@Test | |||
public void failOnMissingComponent() { | |||
executor.runCpdAnalysis(null, "unknown", Collections.emptyList(), 1); | |||
readDuplications(0); | |||
assertThat(logTester.logs(LoggerLevel.ERROR)).contains("Resource not found in component cache: unknown. Skipping CPD computation for it"); | |||
} | |||
@Test | |||
public void timeout() { | |||
for (int i = 1; i <= 2; i++) { | |||
BatchComponent component = createComponent("src/Foo" + i + ".php", 100); | |||
List<Block> blocks = new ArrayList<>(); | |||
for (int j = 1; j <= 100000; j++) { | |||
blocks.add(Block.builder() | |||
.setResourceId(component.key()) | |||
.setIndexInFile(j) | |||
.setLines(j, j + 1) | |||
.setUnit(j, j + 1) | |||
.setBlockHash(new ByteArray("abcd1234".getBytes())) | |||
.build()); | |||
} | |||
index.insert((InputFile) component.inputComponent(), blocks); | |||
} | |||
executor.execute(1); | |||
readDuplications(0); | |||
assertThat(logTester.logs(LoggerLevel.WARN)) | |||
.usingElementComparator((l, r) -> l.matches(r) ? 0 : 1) | |||
.containsOnly( | |||
"Timeout during detection of duplications for .*Foo1.php", | |||
"Timeout during detection of duplications for .*Foo2.php"); | |||
} | |||
private Duplication[] readDuplications(int expected) { | |||
assertThat(reader.readComponentDuplications(batchComponent1.batchId())).hasSize(expected); | |||
Duplication[] duplications = new Duplication[expected]; |