diff options
author | Julien HENRY <julien.henry@sonarsource.com> | 2015-01-21 11:22:44 +0100 |
---|---|---|
committer | Julien HENRY <julien.henry@sonarsource.com> | 2015-01-23 09:59:47 +0100 |
commit | 1340ee7da7a1688ebb059812504e117d041e0124 (patch) | |
tree | 171deefeac6057e876106131ac333bd4914d9253 /plugins | |
parent | 97ca9e16fc27e19f021558502fdce23fe9a77460 (diff) | |
download | sonarqube-1340ee7da7a1688ebb059812504e117d041e0124.tar.gz sonarqube-1340ee7da7a1688ebb059812504e117d041e0124.zip |
SONAR-6012 Local issue tracking
Diffstat (limited to 'plugins')
28 files changed, 12 insertions, 2890 deletions
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java index 9040b071b1a..2187ca1cf85 100644 --- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java +++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java @@ -34,11 +34,6 @@ import org.sonar.plugins.core.dashboards.ProjectIssuesDashboard; import org.sonar.plugins.core.dashboards.ProjectTimeMachineDashboard; import org.sonar.plugins.core.issue.CountFalsePositivesDecorator; import org.sonar.plugins.core.issue.CountUnresolvedIssuesDecorator; -import org.sonar.plugins.core.issue.InitialOpenIssuesSensor; -import org.sonar.plugins.core.issue.InitialOpenIssuesStack; -import org.sonar.plugins.core.issue.IssueHandlers; -import org.sonar.plugins.core.issue.IssueTracking; -import org.sonar.plugins.core.issue.IssueTrackingDecorator; import org.sonar.plugins.core.measurefilters.MyFavouritesFilter; import org.sonar.plugins.core.measurefilters.ProjectFilter; import org.sonar.plugins.core.notifications.alerts.NewAlerts; @@ -325,13 +320,8 @@ public final class CorePlugin extends SonarPlugin { DistributionAreaChart.class, // issues - IssueTrackingDecorator.class, - IssueTracking.class, - IssueHandlers.class, CountUnresolvedIssuesDecorator.class, CountFalsePositivesDecorator.class, - InitialOpenIssuesSensor.class, - InitialOpenIssuesStack.class, HotspotMostViolatedRulesWidget.class, MyUnresolvedIssuesWidget.class, FalsePositiveIssuesWidget.class, diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/InitialOpenIssuesSensor.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/InitialOpenIssuesSensor.java deleted file mode 100644 index 17f22757db8..00000000000 --- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/InitialOpenIssuesSensor.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2014 SonarSource - * mailto:contact AT sonarsource DOT com - * - * SonarQube 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. - * - * SonarQube 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.plugins.core.issue; - -import org.apache.commons.lang.time.DateUtils; -import org.apache.ibatis.session.ResultContext; -import org.apache.ibatis.session.ResultHandler; -import org.sonar.api.batch.Sensor; -import org.sonar.api.batch.SensorContext; -import org.sonar.api.resources.Project; -import org.sonar.core.issue.db.IssueChangeDao; -import org.sonar.core.issue.db.IssueChangeDto; -import org.sonar.core.issue.db.IssueDao; -import org.sonar.core.issue.db.IssueDto; - -import java.util.Calendar; -import java.util.Date; - -/** - * Load all the issues referenced during the previous scan. - */ -public class InitialOpenIssuesSensor implements Sensor { - - private final InitialOpenIssuesStack initialOpenIssuesStack; - private final IssueDao issueDao; - private final IssueChangeDao issueChangeDao; - - public InitialOpenIssuesSensor(InitialOpenIssuesStack initialOpenIssuesStack, IssueDao issueDao, IssueChangeDao issueChangeDao) { - this.initialOpenIssuesStack = initialOpenIssuesStack; - this.issueDao = issueDao; - this.issueChangeDao = issueChangeDao; - } - - @Override - public boolean shouldExecuteOnProject(Project project) { - return true; - } - - @Override - public void analyse(Project project, SensorContext context) { - // Adding one second is a hack for resolving conflicts with concurrent user - // changes during issue persistence - final Date now = DateUtils.addSeconds(DateUtils.truncate(new Date(), Calendar.MILLISECOND), 1); - - issueDao.selectNonClosedIssuesByModule(project.getId(), new ResultHandler() { - @Override - public void handleResult(ResultContext rc) { - IssueDto dto = (IssueDto) rc.getResultObject(); - dto.setSelectedAt(now.getTime()); - initialOpenIssuesStack.addIssue(dto); - } - }); - - issueChangeDao.selectChangelogOnNonClosedIssuesByModuleAndType(project.getId(), new ResultHandler() { - @Override - public void handleResult(ResultContext rc) { - IssueChangeDto dto = (IssueChangeDto) rc.getResultObject(); - initialOpenIssuesStack.addChangelog(dto); - } - }); - } - - @Override - public String toString() { - return getClass().getSimpleName(); - } -} diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/InitialOpenIssuesStack.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/InitialOpenIssuesStack.java deleted file mode 100644 index 1d6f3592547..00000000000 --- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/InitialOpenIssuesStack.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2014 SonarSource - * mailto:contact AT sonarsource DOT com - * - * SonarQube 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. - * - * SonarQube 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.plugins.core.issue; - -import java.util.Collections; -import org.sonar.api.BatchExtension; -import org.sonar.api.batch.InstantiationStrategy; -import org.sonar.batch.index.Cache; -import org.sonar.batch.index.Caches; -import org.sonar.core.issue.db.IssueChangeDto; -import org.sonar.core.issue.db.IssueDto; - -import java.util.ArrayList; -import java.util.List; - -import static com.google.common.collect.Lists.newArrayList; - -@InstantiationStrategy(InstantiationStrategy.PER_BATCH) -public class InitialOpenIssuesStack implements BatchExtension { - - private final Cache<IssueDto> issuesCache; - private final Cache<ArrayList<IssueChangeDto>> issuesChangelogCache; - - public InitialOpenIssuesStack(Caches caches) { - issuesCache = caches.createCache("last-open-issues"); - issuesChangelogCache = caches.createCache("issues-changelog"); - } - - public InitialOpenIssuesStack addIssue(IssueDto issueDto) { - issuesCache.put(issueDto.getComponentKey(), issueDto.getKee(), issueDto); - return this; - } - - public List<IssueDto> selectAndRemoveIssues(String componentKey) { - Iterable<IssueDto> issues = issuesCache.values(componentKey); - List<IssueDto> result = newArrayList(); - for (IssueDto issue : issues) { - result.add(issue); - } - issuesCache.clear(componentKey); - return result; - } - - public Iterable<IssueDto> selectAllIssues() { - return issuesCache.values(); - } - - public InitialOpenIssuesStack addChangelog(IssueChangeDto issueChangeDto) { - List<IssueChangeDto> changeDtos = issuesChangelogCache.get(issueChangeDto.getIssueKey()); - if (changeDtos == null) { - changeDtos = newArrayList(); - } - changeDtos.add(issueChangeDto); - issuesChangelogCache.put(issueChangeDto.getIssueKey(), newArrayList(changeDtos)); - return this; - } - - public List<IssueChangeDto> selectChangelog(String issueKey) { - List<IssueChangeDto> changeDtos = issuesChangelogCache.get(issueKey); - return changeDtos != null ? changeDtos : Collections.<IssueChangeDto>emptyList(); - } - - public void clear() { - issuesCache.clear(); - issuesChangelogCache.clear(); - } -} diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/IssueHandlers.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/IssueHandlers.java deleted file mode 100644 index d416b57f061..00000000000 --- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/IssueHandlers.java +++ /dev/null @@ -1,140 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2014 SonarSource - * mailto:contact AT sonarsource DOT com - * - * SonarQube 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. - * - * SonarQube 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.plugins.core.issue; - -import org.sonar.api.BatchExtension; -import org.sonar.api.issue.Issue; -import org.sonar.api.issue.IssueHandler; -import org.sonar.api.issue.internal.DefaultIssue; -import org.sonar.api.issue.internal.IssueChangeContext; -import org.sonar.api.user.User; -import org.sonar.core.issue.IssueUpdater; -import org.sonar.core.user.DefaultUser; - -import javax.annotation.Nullable; - -public class IssueHandlers implements BatchExtension { - private final IssueHandler[] handlers; - private final DefaultContext context; - - public IssueHandlers(IssueUpdater updater, IssueHandler[] handlers) { - this.handlers = handlers; - this.context = new DefaultContext(updater); - } - - public IssueHandlers(IssueUpdater updater) { - this(updater, new IssueHandler[0]); - } - - public void execute(DefaultIssue issue, IssueChangeContext changeContext) { - context.reset(issue, changeContext); - for (IssueHandler handler : handlers) { - handler.onIssue(context); - } - } - - static class DefaultContext implements IssueHandler.Context { - private final IssueUpdater updater; - private DefaultIssue issue; - private IssueChangeContext changeContext; - - private DefaultContext(IssueUpdater updater) { - this.updater = updater; - } - - private void reset(DefaultIssue i, IssueChangeContext changeContext) { - this.issue = i; - this.changeContext = changeContext; - } - - @Override - public Issue issue() { - return issue; - } - - @Override - public boolean isNew() { - return issue.isNew(); - } - - @Override - public boolean isEndOfLife() { - return issue.isEndOfLife(); - } - - @Override - public IssueHandler.Context setLine(@Nullable Integer line) { - updater.setLine(issue, line); - return this; - } - - @Override - public IssueHandler.Context setMessage(@Nullable String s) { - updater.setMessage(issue, s, changeContext); - return this; - } - - @Override - public IssueHandler.Context setSeverity(String severity) { - updater.setSeverity(issue, severity, changeContext); - return this; - } - - @Override - public IssueHandler.Context setAuthorLogin(@Nullable String login) { - updater.setAuthorLogin(issue, login, changeContext); - return this; - } - - @Override - public IssueHandler.Context setEffortToFix(@Nullable Double d) { - updater.setEffortToFix(issue, d, changeContext); - return this; - } - - @Override - public IssueHandler.Context setAttribute(String key, @Nullable String value) { - throw new UnsupportedOperationException("TODO"); - } - - @Override - public IssueHandler.Context assign(@Nullable String assignee) { - User user = null; - if(assignee != null) { - user = new DefaultUser().setLogin(assignee).setName(assignee); - } - updater.assign(issue, user, changeContext); - return this; - } - - @Override - public IssueHandler.Context assign(@Nullable User user) { - updater.assign(issue, user, changeContext); - return this; - } - - @Override - public IssueHandler.Context addComment(String text) { - updater.addComment(issue, text, changeContext); - return this; - } - } - -} diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/IssueTracking.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/IssueTracking.java deleted file mode 100644 index da4059eeb0d..00000000000 --- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/IssueTracking.java +++ /dev/null @@ -1,340 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2014 SonarSource - * mailto:contact AT sonarsource DOT com - * - * SonarQube 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. - * - * SonarQube 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.plugins.core.issue; - -import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Objects; -import com.google.common.collect.LinkedHashMultimap; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import com.google.common.collect.Multimap; -import org.sonar.api.BatchExtension; -import org.sonar.api.issue.internal.DefaultIssue; -import org.sonar.api.rule.RuleKey; -import org.sonar.core.issue.db.IssueDto; -import org.sonar.plugins.core.issue.tracking.FileHashes; -import org.sonar.plugins.core.issue.tracking.IssueTrackingBlocksRecognizer; -import org.sonar.plugins.core.issue.tracking.RollingFileHashes; - -import javax.annotation.Nullable; - -import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; -import java.util.Map; - -public class IssueTracking implements BatchExtension { - - /** - * @param sourceHashHolder Null when working on resource that is not a file (directory/project) - */ - public IssueTrackingResult track(@Nullable SourceHashHolder sourceHashHolder, Collection<IssueDto> dbIssues, Collection<DefaultIssue> newIssues) { - IssueTrackingResult result = new IssueTrackingResult(); - - if (sourceHashHolder != null) { - setChecksumOnNewIssues(newIssues, sourceHashHolder); - } - - // Map new issues with old ones - mapIssues(newIssues, dbIssues, sourceHashHolder, result); - return result; - } - - private void setChecksumOnNewIssues(Collection<DefaultIssue> issues, SourceHashHolder sourceHashHolder) { - if (issues.isEmpty()) { - return; - } - for (DefaultIssue issue : issues) { - Integer line = issue.line(); - if (line != null) { - issue.setChecksum(sourceHashHolder.getHashedSource().getHash(line)); - } - } - } - - @VisibleForTesting - void mapIssues(Collection<DefaultIssue> newIssues, @Nullable Collection<IssueDto> lastIssues, @Nullable SourceHashHolder sourceHashHolder, IssueTrackingResult result) { - boolean hasLastScan = false; - - if (lastIssues != null) { - hasLastScan = true; - mapLastIssues(newIssues, lastIssues, result); - } - - // If each new issue matches an old one we can stop the matching mechanism - if (result.matched().size() != newIssues.size()) { - if (sourceHashHolder != null && hasLastScan) { - FileHashes hashedReference = sourceHashHolder.getHashedReference(); - if (hashedReference != null) { - mapNewissues(hashedReference, sourceHashHolder.getHashedSource(), newIssues, result); - } - } - mapIssuesOnSameRule(newIssues, result); - } - } - - private void mapLastIssues(Collection<DefaultIssue> newIssues, Collection<IssueDto> lastIssues, IssueTrackingResult result) { - for (IssueDto lastIssue : lastIssues) { - result.addUnmatched(lastIssue); - } - - // Match the key of the issue. (For manual issues) - for (DefaultIssue newIssue : newIssues) { - mapIssue(newIssue, result.unmatchedByKeyForRule(newIssue.ruleKey()).get(newIssue.key()), result); - } - - // Try first to match issues on same rule with same line and with same checksum (but not necessarily with same message) - for (DefaultIssue newIssue : newIssues) { - if (isNotAlreadyMapped(newIssue, result)) { - mapIssue( - newIssue, - findLastIssueWithSameLineAndChecksum(newIssue, result), - result); - } - } - } - - private void mapNewissues(FileHashes hashedReference, FileHashes hashedSource, Collection<DefaultIssue> newIssues, IssueTrackingResult result) { - - IssueTrackingBlocksRecognizer rec = new IssueTrackingBlocksRecognizer(hashedReference, hashedSource); - - RollingFileHashes a = RollingFileHashes.create(hashedReference, 5); - RollingFileHashes b = RollingFileHashes.create(hashedSource, 5); - - Multimap<Integer, DefaultIssue> newIssuesByLines = newIssuesByLines(newIssues, rec, result); - Multimap<Integer, IssueDto> lastIssuesByLines = lastIssuesByLines(result.unmatched(), rec); - - Map<Integer, HashOccurrence> map = Maps.newHashMap(); - - for (Integer line : lastIssuesByLines.keySet()) { - int hash = a.getHash(line); - HashOccurrence hashOccurrence = map.get(hash); - if (hashOccurrence == null) { - // first occurrence in A - hashOccurrence = new HashOccurrence(); - hashOccurrence.lineA = line; - hashOccurrence.countA = 1; - map.put(hash, hashOccurrence); - } else { - hashOccurrence.countA++; - } - } - - for (Integer line : newIssuesByLines.keySet()) { - int hash = b.getHash(line); - HashOccurrence hashOccurrence = map.get(hash); - if (hashOccurrence != null) { - hashOccurrence.lineB = line; - hashOccurrence.countB++; - } - } - - for (HashOccurrence hashOccurrence : map.values()) { - if (hashOccurrence.countA == 1 && hashOccurrence.countB == 1) { - // Guaranteed that lineA has been moved to lineB, so we can map all issues on lineA to all issues on lineB - map(newIssuesByLines.get(hashOccurrence.lineB), lastIssuesByLines.get(hashOccurrence.lineA), result); - lastIssuesByLines.removeAll(hashOccurrence.lineA); - newIssuesByLines.removeAll(hashOccurrence.lineB); - } - } - - // Check if remaining number of lines exceeds threshold - if (lastIssuesByLines.keySet().size() * newIssuesByLines.keySet().size() < 250000) { - List<LinePair> possibleLinePairs = Lists.newArrayList(); - for (Integer oldLine : lastIssuesByLines.keySet()) { - for (Integer newLine : newIssuesByLines.keySet()) { - int weight = rec.computeLengthOfMaximalBlock(oldLine, newLine); - possibleLinePairs.add(new LinePair(oldLine, newLine, weight)); - } - } - Collections.sort(possibleLinePairs, LINE_PAIR_COMPARATOR); - for (LinePair linePair : possibleLinePairs) { - // High probability that lineA has been moved to lineB, so we can map all Issues on lineA to all Issues on lineB - map(newIssuesByLines.get(linePair.lineB), lastIssuesByLines.get(linePair.lineA), result); - } - } - } - - private void mapIssuesOnSameRule(Collection<DefaultIssue> newIssues, IssueTrackingResult result) { - // Try then to match issues on same rule with same message and with same checksum - for (DefaultIssue newIssue : newIssues) { - if (isNotAlreadyMapped(newIssue, result)) { - mapIssue( - newIssue, - findLastIssueWithSameChecksumAndMessage(newIssue, result.unmatchedByKeyForRule(newIssue.ruleKey()).values()), - result); - } - } - - // Try then to match issues on same rule with same line and with same message - for (DefaultIssue newIssue : newIssues) { - if (isNotAlreadyMapped(newIssue, result)) { - mapIssue( - newIssue, - findLastIssueWithSameLineAndMessage(newIssue, result.unmatchedByKeyForRule(newIssue.ruleKey()).values()), - result); - } - } - - // Last check: match issue if same rule and same checksum but different line and different message - // See SONAR-2812 - for (DefaultIssue newIssue : newIssues) { - if (isNotAlreadyMapped(newIssue, result)) { - mapIssue( - newIssue, - findLastIssueWithSameChecksum(newIssue, result.unmatchedByKeyForRule(newIssue.ruleKey()).values()), - result); - } - } - } - - private void map(Collection<DefaultIssue> newIssues, Collection<IssueDto> lastIssues, IssueTrackingResult result) { - for (DefaultIssue newIssue : newIssues) { - if (isNotAlreadyMapped(newIssue, result)) { - for (IssueDto pastIssue : lastIssues) { - if (isNotAlreadyMapped(pastIssue, result) && Objects.equal(newIssue.ruleKey(), RuleKey.of(pastIssue.getRuleRepo(), pastIssue.getRule()))) { - mapIssue(newIssue, pastIssue, result); - break; - } - } - } - } - } - - private Multimap<Integer, DefaultIssue> newIssuesByLines(Collection<DefaultIssue> newIssues, IssueTrackingBlocksRecognizer rec, IssueTrackingResult result) { - Multimap<Integer, DefaultIssue> newIssuesByLines = LinkedHashMultimap.create(); - for (DefaultIssue newIssue : newIssues) { - if (isNotAlreadyMapped(newIssue, result) && rec.isValidLineInSource(newIssue.line())) { - newIssuesByLines.put(newIssue.line(), newIssue); - } - } - return newIssuesByLines; - } - - private Multimap<Integer, IssueDto> lastIssuesByLines(Collection<IssueDto> lastIssues, IssueTrackingBlocksRecognizer rec) { - Multimap<Integer, IssueDto> lastIssuesByLines = LinkedHashMultimap.create(); - for (IssueDto pastIssue : lastIssues) { - if (rec.isValidLineInReference(pastIssue.getLine())) { - lastIssuesByLines.put(pastIssue.getLine(), pastIssue); - } - } - return lastIssuesByLines; - } - - private IssueDto findLastIssueWithSameChecksum(DefaultIssue newIssue, Collection<IssueDto> lastIssues) { - for (IssueDto pastIssue : lastIssues) { - if (isSameChecksum(newIssue, pastIssue)) { - return pastIssue; - } - } - return null; - } - - private IssueDto findLastIssueWithSameLineAndMessage(DefaultIssue newIssue, Collection<IssueDto> lastIssues) { - for (IssueDto pastIssue : lastIssues) { - if (isSameLine(newIssue, pastIssue) && isSameMessage(newIssue, pastIssue)) { - return pastIssue; - } - } - return null; - } - - private IssueDto findLastIssueWithSameChecksumAndMessage(DefaultIssue newIssue, Collection<IssueDto> lastIssues) { - for (IssueDto pastIssue : lastIssues) { - if (isSameChecksum(newIssue, pastIssue) && isSameMessage(newIssue, pastIssue)) { - return pastIssue; - } - } - return null; - } - - private IssueDto findLastIssueWithSameLineAndChecksum(DefaultIssue newIssue, IssueTrackingResult result) { - Collection<IssueDto> sameRuleAndSameLineAndSameChecksum = result.unmatchedForRuleAndForLineAndForChecksum(newIssue.ruleKey(), newIssue.line(), newIssue.checksum()); - if (!sameRuleAndSameLineAndSameChecksum.isEmpty()) { - return sameRuleAndSameLineAndSameChecksum.iterator().next(); - } - return null; - } - - private boolean isNotAlreadyMapped(IssueDto pastIssue, IssueTrackingResult result) { - return result.unmatched().contains(pastIssue); - } - - private boolean isNotAlreadyMapped(DefaultIssue newIssue, IssueTrackingResult result) { - return !result.isMatched(newIssue); - } - - private boolean isSameChecksum(DefaultIssue newIssue, IssueDto pastIssue) { - return Objects.equal(pastIssue.getChecksum(), newIssue.checksum()); - } - - private boolean isSameLine(DefaultIssue newIssue, IssueDto pastIssue) { - return Objects.equal(pastIssue.getLine(), newIssue.line()); - } - - private boolean isSameMessage(DefaultIssue newIssue, IssueDto pastIssue) { - return Objects.equal(newIssue.message(), pastIssue.getMessage()); - } - - private void mapIssue(DefaultIssue issue, @Nullable IssueDto ref, IssueTrackingResult result) { - if (ref != null) { - result.setMatch(issue, ref); - } - } - - @Override - public String toString() { - return getClass().getSimpleName(); - } - - private static class LinePair { - int lineA; - int lineB; - int weight; - - public LinePair(int lineA, int lineB, int weight) { - this.lineA = lineA; - this.lineB = lineB; - this.weight = weight; - } - } - - private static class HashOccurrence { - int lineA; - int lineB; - int countA; - int countB; - } - - private static final Comparator<LinePair> LINE_PAIR_COMPARATOR = new Comparator<LinePair>() { - @Override - public int compare(LinePair o1, LinePair o2) { - int weightDiff = o2.weight - o1.weight; - if (weightDiff != 0) { - return weightDiff; - } else { - return Math.abs(o1.lineA - o1.lineB) - Math.abs(o2.lineA - o2.lineB); - } - } - }; - -} diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/IssueTrackingDecorator.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/IssueTrackingDecorator.java deleted file mode 100644 index 3d2bfc48e9d..00000000000 --- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/IssueTrackingDecorator.java +++ /dev/null @@ -1,277 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2014 SonarSource - * mailto:contact AT sonarsource DOT com - * - * SonarQube 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. - * - * SonarQube 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.plugins.core.issue; - -import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Strings; -import com.google.common.collect.Lists; -import org.apache.commons.lang.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.sonar.api.batch.Decorator; -import org.sonar.api.batch.DecoratorBarriers; -import org.sonar.api.batch.DecoratorContext; -import org.sonar.api.batch.DependedUpon; -import org.sonar.api.batch.DependsUpon; -import org.sonar.api.batch.fs.InputFile; -import org.sonar.api.batch.fs.internal.DefaultInputFile; -import org.sonar.api.component.ResourcePerspectives; -import org.sonar.api.issue.Issuable; -import org.sonar.api.issue.Issue; -import org.sonar.api.issue.internal.DefaultIssue; -import org.sonar.api.issue.internal.IssueChangeContext; -import org.sonar.api.profiles.RulesProfile; -import org.sonar.api.resources.File; -import org.sonar.api.resources.Project; -import org.sonar.api.resources.Resource; -import org.sonar.api.resources.ResourceUtils; -import org.sonar.api.rules.ActiveRule; -import org.sonar.api.rules.Rule; -import org.sonar.api.rules.RuleFinder; -import org.sonar.api.utils.Duration; -import org.sonar.api.utils.KeyValueFormat; -import org.sonar.batch.issue.IssueCache; -import org.sonar.batch.scan.LastLineHashes; -import org.sonar.batch.scan.filesystem.InputPathCache; -import org.sonar.core.issue.IssueUpdater; -import org.sonar.core.issue.db.IssueChangeDto; -import org.sonar.core.issue.db.IssueDto; -import org.sonar.core.issue.workflow.IssueWorkflow; - -import java.util.Collection; - -@DependsUpon(DecoratorBarriers.ISSUES_ADDED) -@DependedUpon(DecoratorBarriers.ISSUES_TRACKED) -public class IssueTrackingDecorator implements Decorator { - - private static final Logger LOG = LoggerFactory.getLogger(IssueTrackingDecorator.class); - - private final IssueCache issueCache; - private final InitialOpenIssuesStack initialOpenIssues; - private final IssueTracking tracking; - private final LastLineHashes lastLineHashes; - private final IssueHandlers handlers; - private final IssueWorkflow workflow; - private final IssueUpdater updater; - private final IssueChangeContext changeContext; - private final ResourcePerspectives perspectives; - private final RulesProfile rulesProfile; - private final RuleFinder ruleFinder; - private final InputPathCache inputPathCache; - private final Project project; - - public IssueTrackingDecorator(IssueCache issueCache, InitialOpenIssuesStack initialOpenIssues, IssueTracking tracking, - LastLineHashes lastLineHashes, - IssueHandlers handlers, IssueWorkflow workflow, - IssueUpdater updater, - Project project, - ResourcePerspectives perspectives, - RulesProfile rulesProfile, - RuleFinder ruleFinder, InputPathCache inputPathCache) { - this.issueCache = issueCache; - this.initialOpenIssues = initialOpenIssues; - this.tracking = tracking; - this.lastLineHashes = lastLineHashes; - this.handlers = handlers; - this.workflow = workflow; - this.updater = updater; - this.project = project; - this.inputPathCache = inputPathCache; - this.changeContext = IssueChangeContext.createScan(project.getAnalysisDate()); - this.perspectives = perspectives; - this.rulesProfile = rulesProfile; - this.ruleFinder = ruleFinder; - } - - @Override - public boolean shouldExecuteOnProject(Project project) { - return true; - } - - @Override - public void decorate(Resource resource, DecoratorContext context) { - Issuable issuable = perspectives.as(Issuable.class, resource); - if (issuable != null) { - doDecorate(resource); - } - } - - @VisibleForTesting - void doDecorate(Resource resource) { - Collection<DefaultIssue> issues = Lists.newArrayList(); - for (Issue issue : issueCache.byComponent(resource.getEffectiveKey())) { - issues.add((DefaultIssue) issue); - } - issueCache.clear(resource.getEffectiveKey()); - // issues = all the issues created by rule engines during this module scan and not excluded by filters - - // all the issues that are not closed in db before starting this module scan, including manual issues - Collection<IssueDto> dbOpenIssues = initialOpenIssues.selectAndRemoveIssues(resource.getEffectiveKey()); - - SourceHashHolder sourceHashHolder = null; - if (ResourceUtils.isFile(resource)) { - File sonarFile = (File) resource; - InputFile file = inputPathCache.getFile(project.getEffectiveKey(), sonarFile.getPath()); - if (file == null) { - throw new IllegalStateException("Resource " + resource + " was not found in InputPath cache"); - } - sourceHashHolder = new SourceHashHolder((DefaultInputFile) file, lastLineHashes); - } - - IssueTrackingResult trackingResult = tracking.track(sourceHashHolder, dbOpenIssues, issues); - - // unmatched = issues that have been resolved + issues on disabled/removed rules + manual issues - addUnmatched(trackingResult.unmatched(), sourceHashHolder, issues); - - mergeMatched(trackingResult); - - if (ResourceUtils.isProject(resource)) { - // issues that relate to deleted components - addIssuesOnDeletedComponents(issues); - } - - for (DefaultIssue issue : issues) { - workflow.doAutomaticTransition(issue, changeContext); - handlers.execute(issue, changeContext); - issueCache.put(issue); - } - } - - @VisibleForTesting - protected void mergeMatched(IssueTrackingResult result) { - for (DefaultIssue issue : result.matched()) { - IssueDto ref = result.matching(issue); - - // invariant fields - issue.setKey(ref.getKee()); - issue.setCreationDate(ref.getIssueCreationDate()); - issue.setUpdateDate(ref.getIssueUpdateDate()); - issue.setCloseDate(ref.getIssueCloseDate()); - - // non-persisted fields - issue.setNew(false); - issue.setEndOfLife(false); - issue.setOnDisabledRule(false); - issue.setSelectedAt(ref.getSelectedAt()); - - // fields to update with old values - issue.setActionPlanKey(ref.getActionPlanKey()); - issue.setResolution(ref.getResolution()); - issue.setStatus(ref.getStatus()); - issue.setAssignee(ref.getAssignee()); - issue.setAuthorLogin(ref.getAuthorLogin()); - issue.setTags(ref.getTags()); - - if (ref.getIssueAttributes() != null) { - issue.setAttributes(KeyValueFormat.parse(ref.getIssueAttributes())); - } - - // populate existing changelog - Collection<IssueChangeDto> issueChangeDtos = initialOpenIssues.selectChangelog(issue.key()); - for (IssueChangeDto issueChangeDto : issueChangeDtos) { - issue.addChange(issueChangeDto.toFieldDiffs()); - } - - // fields to update with current values - if (ref.isManualSeverity()) { - issue.setManualSeverity(true); - issue.setSeverity(ref.getSeverity()); - } else { - updater.setPastSeverity(issue, ref.getSeverity(), changeContext); - } - updater.setPastLine(issue, ref.getLine()); - updater.setPastMessage(issue, ref.getMessage(), changeContext); - updater.setPastEffortToFix(issue, ref.getEffortToFix(), changeContext); - Long debtInMinutes = ref.getDebt(); - Duration previousTechnicalDebt = debtInMinutes != null ? Duration.create(debtInMinutes) : null; - updater.setPastTechnicalDebt(issue, previousTechnicalDebt, changeContext); - updater.setPastProject(issue, ref.getProjectKey(), changeContext); - } - } - - private void addUnmatched(Collection<IssueDto> unmatchedIssues, SourceHashHolder sourceHashHolder, Collection<DefaultIssue> issues) { - for (IssueDto unmatchedDto : unmatchedIssues) { - DefaultIssue unmatched = unmatchedDto.toDefaultIssue(); - if (StringUtils.isNotBlank(unmatchedDto.getReporter()) && !Issue.STATUS_CLOSED.equals(unmatchedDto.getStatus())) { - relocateManualIssue(unmatched, unmatchedDto, sourceHashHolder); - } - updateUnmatchedIssue(unmatched, false /* manual issues can be kept open */); - issues.add(unmatched); - } - } - - private void addIssuesOnDeletedComponents(Collection<DefaultIssue> issues) { - for (IssueDto deadDto : initialOpenIssues.selectAllIssues()) { - DefaultIssue dead = deadDto.toDefaultIssue(); - updateUnmatchedIssue(dead, true); - issues.add(dead); - } - initialOpenIssues.clear(); - } - - private void updateUnmatchedIssue(DefaultIssue issue, boolean forceEndOfLife) { - issue.setNew(false); - - boolean manualIssue = !Strings.isNullOrEmpty(issue.reporter()); - Rule rule = ruleFinder.findByKey(issue.ruleKey()); - if (manualIssue) { - // Manual rules are not declared in Quality profiles, so no need to check ActiveRule - boolean isRemovedRule = rule == null || Rule.STATUS_REMOVED.equals(rule.getStatus()); - issue.setEndOfLife(forceEndOfLife || isRemovedRule); - issue.setOnDisabledRule(isRemovedRule); - } else { - ActiveRule activeRule = rulesProfile.getActiveRule(issue.ruleKey().repository(), issue.ruleKey().rule()); - issue.setEndOfLife(true); - issue.setOnDisabledRule(activeRule == null || rule == null || Rule.STATUS_REMOVED.equals(rule.getStatus())); - } - } - - private void relocateManualIssue(DefaultIssue newIssue, IssueDto oldIssue, SourceHashHolder sourceHashHolder) { - LOG.debug("Trying to relocate manual issue {}", oldIssue.getKee()); - - Integer previousLine = oldIssue.getLine(); - if (previousLine == null) { - LOG.debug("Cannot relocate issue at resource level"); - return; - } - - Collection<Integer> newLinesWithSameHash = sourceHashHolder.getNewLinesMatching(previousLine); - LOG.debug("Found the following lines with same hash: {}", newLinesWithSameHash); - if (newLinesWithSameHash.isEmpty()) { - if (previousLine > sourceHashHolder.getHashedSource().length()) { - LOG.debug("Old issue line {} is out of new source, closing and removing line number", previousLine); - newIssue.setLine(null); - updater.setStatus(newIssue, Issue.STATUS_CLOSED, changeContext); - updater.setResolution(newIssue, Issue.RESOLUTION_REMOVED, changeContext); - updater.setPastLine(newIssue, previousLine); - updater.setPastMessage(newIssue, oldIssue.getMessage(), changeContext); - updater.setPastEffortToFix(newIssue, oldIssue.getEffortToFix(), changeContext); - } - } else if (newLinesWithSameHash.size() == 1) { - Integer newLine = newLinesWithSameHash.iterator().next(); - LOG.debug("Relocating issue to line {}", newLine); - - newIssue.setLine(newLine); - updater.setPastLine(newIssue, previousLine); - updater.setPastMessage(newIssue, oldIssue.getMessage(), changeContext); - updater.setPastEffortToFix(newIssue, oldIssue.getEffortToFix(), changeContext); - } - } -} diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/IssueTrackingResult.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/IssueTrackingResult.java deleted file mode 100644 index f63f94f8115..00000000000 --- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/IssueTrackingResult.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2014 SonarSource - * mailto:contact AT sonarsource DOT com - * - * SonarQube 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. - * - * SonarQube 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.plugins.core.issue; - -import com.google.common.collect.HashMultimap; -import com.google.common.collect.Maps; -import com.google.common.collect.Multimap; -import org.apache.commons.lang.StringUtils; -import org.sonar.api.issue.internal.DefaultIssue; -import org.sonar.api.rule.RuleKey; -import org.sonar.core.issue.db.IssueDto; - -import javax.annotation.Nullable; - -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - -class IssueTrackingResult { - private final Map<String, IssueDto> unmatchedByKey = new HashMap<String, IssueDto>(); - private final Map<RuleKey, Map<String, IssueDto>> unmatchedByRuleAndKey = new HashMap<RuleKey, Map<String, IssueDto>>(); - private final Map<RuleKey, Map<Integer, Multimap<String, IssueDto>>> unmatchedByRuleAndLineAndChecksum = - new HashMap<RuleKey, Map<Integer, Multimap<String, IssueDto>>>(); - private final Map<DefaultIssue, IssueDto> matched = Maps.newIdentityHashMap(); - - Collection<IssueDto> unmatched() { - return unmatchedByKey.values(); - } - - Map<String, IssueDto> unmatchedByKeyForRule(RuleKey ruleKey) { - return unmatchedByRuleAndKey.containsKey(ruleKey) ? unmatchedByRuleAndKey.get(ruleKey) : Collections.<String, IssueDto>emptyMap(); - } - - Collection<IssueDto> unmatchedForRuleAndForLineAndForChecksum(RuleKey ruleKey, @Nullable Integer line, @Nullable String checksum) { - if (!unmatchedByRuleAndLineAndChecksum.containsKey(ruleKey)) { - return Collections.emptyList(); - } - Map<Integer, Multimap<String, IssueDto>> unmatchedForRule = unmatchedByRuleAndLineAndChecksum.get(ruleKey); - Integer lineNotNull = line != null ? line : 0; - if (!unmatchedForRule.containsKey(lineNotNull)) { - return Collections.emptyList(); - } - Multimap<String, IssueDto> unmatchedForRuleAndLine = unmatchedForRule.get(lineNotNull); - String checksumNotNull = StringUtils.defaultString(checksum, ""); - if (!unmatchedForRuleAndLine.containsKey(checksumNotNull)) { - return Collections.emptyList(); - } - return unmatchedForRuleAndLine.get(checksumNotNull); - } - - Collection<DefaultIssue> matched() { - return matched.keySet(); - } - - boolean isMatched(DefaultIssue issue) { - return matched.containsKey(issue); - } - - IssueDto matching(DefaultIssue issue) { - return matched.get(issue); - } - - void addUnmatched(IssueDto i) { - unmatchedByKey.put(i.getKee(), i); - RuleKey ruleKey = RuleKey.of(i.getRuleRepo(), i.getRule()); - if (!unmatchedByRuleAndKey.containsKey(ruleKey)) { - unmatchedByRuleAndKey.put(ruleKey, new HashMap<String, IssueDto>()); - unmatchedByRuleAndLineAndChecksum.put(ruleKey, new HashMap<Integer, Multimap<String, IssueDto>>()); - } - unmatchedByRuleAndKey.get(ruleKey).put(i.getKee(), i); - Map<Integer, Multimap<String, IssueDto>> unmatchedForRule = unmatchedByRuleAndLineAndChecksum.get(ruleKey); - Integer lineNotNull = lineNotNull(i); - if (!unmatchedForRule.containsKey(lineNotNull)) { - unmatchedForRule.put(lineNotNull, HashMultimap.<String, IssueDto>create()); - } - Multimap<String, IssueDto> unmatchedForRuleAndLine = unmatchedForRule.get(lineNotNull); - String checksumNotNull = StringUtils.defaultString(i.getChecksum(), ""); - unmatchedForRuleAndLine.put(checksumNotNull, i); - } - - private Integer lineNotNull(IssueDto i) { - Integer line = i.getLine(); - return line != null ? line : 0; - } - - void setMatch(DefaultIssue issue, IssueDto matching) { - matched.put(issue, matching); - RuleKey ruleKey = RuleKey.of(matching.getRuleRepo(), matching.getRule()); - unmatchedByRuleAndKey.get(ruleKey).remove(matching.getKee()); - unmatchedByKey.remove(matching.getKee()); - Integer lineNotNull = lineNotNull(matching); - String checksumNotNull = StringUtils.defaultString(matching.getChecksum(), ""); - unmatchedByRuleAndLineAndChecksum.get(ruleKey).get(lineNotNull).get(checksumNotNull).remove(matching); - } -} diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/SourceHashHolder.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/SourceHashHolder.java deleted file mode 100644 index 612968e8fc9..00000000000 --- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/SourceHashHolder.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2014 SonarSource - * mailto:contact AT sonarsource DOT com - * - * SonarQube 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. - * - * SonarQube 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.plugins.core.issue; - -import com.google.common.collect.ImmutableSet; -import org.sonar.api.batch.fs.InputFile.Status; -import org.sonar.api.batch.fs.internal.DefaultInputFile; -import org.sonar.batch.scan.LastLineHashes; -import org.sonar.plugins.core.issue.tracking.FileHashes; - -import javax.annotation.CheckForNull; - -import java.util.Collection; - -public class SourceHashHolder { - - private final LastLineHashes lastSnapshots; - - private FileHashes hashedReference; - private FileHashes hashedSource; - private DefaultInputFile inputFile; - - public SourceHashHolder(DefaultInputFile inputFile, LastLineHashes lastSnapshots) { - this.inputFile = inputFile; - this.lastSnapshots = lastSnapshots; - } - - private void initHashes() { - if (hashedSource == null) { - hashedSource = FileHashes.create(inputFile.lineHashes()); - Status status = inputFile.status(); - if (status == Status.ADDED) { - hashedReference = null; - } else if (status == Status.SAME) { - hashedReference = hashedSource; - } else { - String[] lineHashes = lastSnapshots.getLineHashes(inputFile.key()); - hashedReference = lineHashes != null ? FileHashes.create(lineHashes) : null; - } - } - } - - @CheckForNull - public FileHashes getHashedReference() { - initHashes(); - return hashedReference; - } - - public FileHashes getHashedSource() { - initHashes(); - return hashedSource; - } - - public Collection<Integer> getNewLinesMatching(Integer originLine) { - FileHashes reference = getHashedReference(); - if (reference == null) { - return ImmutableSet.of(); - } else { - return getHashedSource().getLinesForHash(reference.getHash(originLine)); - } - } -} diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/tracking/FileHashes.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/tracking/FileHashes.java deleted file mode 100644 index d45ac2d65cc..00000000000 --- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/tracking/FileHashes.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2014 SonarSource - * mailto:contact AT sonarsource DOT com - * - * SonarQube 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. - * - * SonarQube 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.plugins.core.issue.tracking; - -import com.google.common.collect.LinkedHashMultimap; -import com.google.common.collect.Multimap; -import org.apache.commons.codec.binary.Hex; -import org.apache.commons.lang.ObjectUtils; - -import java.util.Collection; - -/** - * Wraps a {@link Sequence} to assign hash codes to elements. - */ -public final class FileHashes { - - private final String[] hashes; - private final Multimap<String, Integer> linesByHash; - - private FileHashes(String[] hashes, Multimap<String, Integer> linesByHash) { - this.hashes = hashes; - this.linesByHash = linesByHash; - } - - public static FileHashes create(String[] hashes) { - int size = hashes.length; - Multimap<String, Integer> linesByHash = LinkedHashMultimap.create(); - for (int i = 0; i < size; i++) { - // indices in array are shifted one line before - linesByHash.put(hashes[i], i + 1); - } - return new FileHashes(hashes, linesByHash); - } - - public static FileHashes create(byte[][] hashes) { - int size = hashes.length; - Multimap<String, Integer> linesByHash = LinkedHashMultimap.create(); - String[] hexHashes = new String[size]; - for (int i = 0; i < size; i++) { - String hash = hashes[i] != null ? Hex.encodeHexString(hashes[i]) : ""; - hexHashes[i] = hash; - // indices in array are shifted one line before - linesByHash.put(hash, i + 1); - } - return new FileHashes(hexHashes, linesByHash); - } - - public int length() { - return hashes.length; - } - - public Collection<Integer> getLinesForHash(String hash) { - return linesByHash.get(hash); - } - - public String getHash(int line) { - // indices in array are shifted one line before - return (String) ObjectUtils.defaultIfNull(hashes[line - 1], ""); - } -} diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/tracking/IssueTrackingBlocksRecognizer.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/tracking/IssueTrackingBlocksRecognizer.java deleted file mode 100644 index 11611ad952d..00000000000 --- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/tracking/IssueTrackingBlocksRecognizer.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2014 SonarSource - * mailto:contact AT sonarsource DOT com - * - * SonarQube 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. - * - * SonarQube 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.plugins.core.issue.tracking; - -import javax.annotation.Nullable; - -public class IssueTrackingBlocksRecognizer { - - private final FileHashes a; - private final FileHashes b; - - public IssueTrackingBlocksRecognizer(FileHashes a, FileHashes b) { - this.a = a; - this.b = b; - } - - public boolean isValidLineInReference(@Nullable Integer line) { - return (line != null) && (0 <= line - 1) && (line - 1 < a.length()); - } - - public boolean isValidLineInSource(@Nullable Integer line) { - return (line != null) && (0 <= line - 1) && (line - 1 < b.length()); - } - - /** - * @param startA number of line from first version of text (numbering starts from 1) - * @param startB number of line from second version of text (numbering starts from 1) - */ - public int computeLengthOfMaximalBlock(int startA, int startB) { - if (!a.getHash(startA).equals(b.getHash(startB))) { - return 0; - } - int length = 0; - int ai = startA; - int bi = startB; - while (ai <= a.length() && bi <= b.length() && a.getHash(ai).equals(b.getHash(bi))) { - ai++; - bi++; - length++; - } - ai = startA; - bi = startB; - while (ai > 0 && bi > 0 && a.getHash(ai).equals(b.getHash(bi))) { - ai--; - bi--; - length++; - } - // Note that position (startA, startB) was counted twice - return length - 1; - } - -} diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/tracking/RollingFileHashes.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/tracking/RollingFileHashes.java deleted file mode 100644 index 313507a4d69..00000000000 --- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/tracking/RollingFileHashes.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2014 SonarSource - * mailto:contact AT sonarsource DOT com - * - * SonarQube 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. - * - * SonarQube 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.plugins.core.issue.tracking; - -/** - * Compute hashes of block around each line - */ -public class RollingFileHashes { - - final int[] rollingHashes; - - public static RollingFileHashes create(FileHashes hashes, int halfBlockSize) { - int size = hashes.length(); - int[] rollingHashes = new int[size]; - - RollingHashCalculator hashCalulator = new RollingHashCalculator(halfBlockSize * 2 + 1); - for (int i = 1; i <= Math.min(size, halfBlockSize + 1); i++) { - hashCalulator.add(hashes.getHash(i).hashCode()); - } - for (int i = 1; i <= size; i++) { - rollingHashes[i - 1] = hashCalulator.getHash(); - if (i - halfBlockSize > 0) { - hashCalulator.remove(hashes.getHash(i - halfBlockSize).hashCode()); - } - if (i + 1 + halfBlockSize <= size) { - hashCalulator.add(hashes.getHash(i + 1 + halfBlockSize).hashCode()); - } else { - hashCalulator.add(0); - } - } - - return new RollingFileHashes(rollingHashes); - } - - public int getHash(int line) { - return rollingHashes[line - 1]; - } - - private RollingFileHashes(int[] hashes) { - this.rollingHashes = hashes; - } - - private static class RollingHashCalculator { - - private static final int PRIME_BASE = 31; - - private final int power; - private int hash; - - public RollingHashCalculator(int size) { - int pow = 1; - for (int i = 0; i < size - 1; i++) { - pow = pow * PRIME_BASE; - } - this.power = pow; - } - - public void add(int value) { - hash = hash * PRIME_BASE + value; - } - - public void remove(int value) { - hash = hash - power * value; - } - - public int getHash() { - return hash; - } - - } - -} diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/tracking/package-info.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/tracking/package-info.java deleted file mode 100644 index 4a3c6986229..00000000000 --- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/tracking/package-info.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2014 SonarSource - * mailto:contact AT sonarsource DOT com - * - * SonarQube 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. - * - * SonarQube 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. - */ - -@javax.annotation.ParametersAreNonnullByDefault -package org.sonar.plugins.core.issue.tracking; - diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/InitialOpenIssuesSensorTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/InitialOpenIssuesSensorTest.java deleted file mode 100644 index e1c70330963..00000000000 --- a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/InitialOpenIssuesSensorTest.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2014 SonarSource - * mailto:contact AT sonarsource DOT com - * - * SonarQube 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. - * - * SonarQube 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.plugins.core.issue; - -import org.apache.ibatis.session.ResultHandler; -import org.junit.Test; -import org.sonar.api.resources.Project; -import org.sonar.core.issue.db.IssueChangeDao; -import org.sonar.core.issue.db.IssueDao; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; - -public class InitialOpenIssuesSensorTest { - - InitialOpenIssuesStack stack = mock(InitialOpenIssuesStack.class); - IssueDao issueDao = mock(IssueDao.class); - IssueChangeDao issueChangeDao = mock(IssueChangeDao.class); - - InitialOpenIssuesSensor sensor = new InitialOpenIssuesSensor(stack, issueDao, issueChangeDao); - - @Test - public void should_select_module_open_issues() { - Project project = new Project("key"); - project.setId(1); - sensor.analyse(project, null); - - verify(issueDao).selectNonClosedIssuesByModule(eq(1), any(ResultHandler.class)); - } - - @Test - public void should_select_module_open_issues_changelog() { - Project project = new Project("key"); - project.setId(1); - sensor.analyse(project, null); - - verify(issueChangeDao).selectChangelogOnNonClosedIssuesByModuleAndType(eq(1), any(ResultHandler.class)); - } - - @Test - public void test_toString() throws Exception { - assertThat(sensor.toString()).isEqualTo("InitialOpenIssuesSensor"); - - } -} diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/InitialOpenIssuesStackTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/InitialOpenIssuesStackTest.java deleted file mode 100644 index ed7782e138b..00000000000 --- a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/InitialOpenIssuesStackTest.java +++ /dev/null @@ -1,142 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2014 SonarSource - * mailto:contact AT sonarsource DOT com - * - * SonarQube 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. - * - * SonarQube 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.plugins.core.issue; - -import org.junit.After; -import org.junit.Before; -import org.junit.ClassRule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; -import org.sonar.api.CoreProperties; -import org.sonar.batch.bootstrap.BootstrapProperties; -import org.sonar.batch.bootstrap.TempFolderProvider; -import org.sonar.batch.index.Caches; -import org.sonar.core.issue.db.IssueChangeDto; -import org.sonar.core.issue.db.IssueDto; - -import java.io.IOException; -import java.util.Collections; -import java.util.List; - -import static org.assertj.core.api.Assertions.assertThat; - -public class InitialOpenIssuesStackTest { - - @ClassRule - public static TemporaryFolder temp = new TemporaryFolder(); - - public static Caches createCacheOnTemp(TemporaryFolder temp) { - BootstrapProperties bootstrapSettings = new BootstrapProperties(Collections.<String, String>emptyMap()); - try { - bootstrapSettings.properties().put(CoreProperties.WORKING_DIRECTORY, temp.newFolder().getAbsolutePath()); - } catch (IOException e) { - throw new RuntimeException(e); - } - return new Caches(new TempFolderProvider().provide(bootstrapSettings)); - } - - InitialOpenIssuesStack stack; - Caches caches; - - @Before - public void before() throws Exception { - caches = createCacheOnTemp(temp); - caches.start(); - stack = new InitialOpenIssuesStack(caches); - } - - @After - public void after() { - caches.stop(); - } - - @Test - public void get_and_remove_issues() { - IssueDto issueDto = new IssueDto().setComponentKey("org.struts.Action").setKee("ISSUE-1"); - stack.addIssue(issueDto); - - List<IssueDto> issueDtos = stack.selectAndRemoveIssues("org.struts.Action"); - assertThat(issueDtos).hasSize(1); - assertThat(issueDtos.get(0).getKee()).isEqualTo("ISSUE-1"); - - assertThat(stack.selectAllIssues()).isEmpty(); - } - - @Test - public void get_and_remove_with_many_issues_on_same_resource() { - stack.addIssue(new IssueDto().setComponentKey("org.struts.Action").setKee("ISSUE-1")); - stack.addIssue(new IssueDto().setComponentKey("org.struts.Action").setKee("ISSUE-2")); - - List<IssueDto> issueDtos = stack.selectAndRemoveIssues("org.struts.Action"); - assertThat(issueDtos).hasSize(2); - - assertThat(stack.selectAllIssues()).isEmpty(); - } - - @Test - public void get_and_remove_do_nothing_if_resource_not_found() { - stack.addIssue(new IssueDto().setComponentKey("org.struts.Action").setKee("ISSUE-1")); - - List<IssueDto> issueDtos = stack.selectAndRemoveIssues("Other"); - assertThat(issueDtos).hasSize(0); - - assertThat(stack.selectAllIssues()).hasSize(1); - } - - @Test - public void select_changelog() { - stack.addChangelog(new IssueChangeDto().setKey("CHANGE-1").setIssueKey("ISSUE-1")); - stack.addChangelog(new IssueChangeDto().setKey("CHANGE-2").setIssueKey("ISSUE-1")); - - List<IssueChangeDto> issueChangeDtos = stack.selectChangelog("ISSUE-1"); - assertThat(issueChangeDtos).hasSize(2); - assertThat(issueChangeDtos.get(0).getKey()).isEqualTo("CHANGE-1"); - assertThat(issueChangeDtos.get(1).getKey()).isEqualTo("CHANGE-2"); - } - - @Test - public void return_empty_changelog() { - assertThat(stack.selectChangelog("ISSUE-1")).isEmpty(); - } - - @Test - public void clear_issues() { - stack.addIssue(new IssueDto().setComponentKey("org.struts.Action").setKee("ISSUE-1")); - - assertThat(stack.selectAllIssues()).hasSize(1); - - // issues are not removed - assertThat(stack.selectAllIssues()).hasSize(1); - - stack.clear(); - assertThat(stack.selectAllIssues()).isEmpty(); - } - - @Test - public void clear_issues_changelog() { - stack.addChangelog(new IssueChangeDto().setKey("CHANGE-1").setIssueKey("ISSUE-1")); - - assertThat(stack.selectChangelog("ISSUE-1")).hasSize(1); - - stack.clear(); - assertThat(stack.selectChangelog("ISSUE-1")).isEmpty(); - } -} diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/IssueHandlersTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/IssueHandlersTest.java deleted file mode 100644 index b1f745927e5..00000000000 --- a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/IssueHandlersTest.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2014 SonarSource - * mailto:contact AT sonarsource DOT com - * - * SonarQube 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. - * - * SonarQube 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.plugins.core.issue; - -import org.junit.Test; -import org.mockito.ArgumentMatcher; -import org.sonar.api.issue.IssueHandler; -import org.sonar.api.issue.internal.DefaultIssue; -import org.sonar.api.issue.internal.IssueChangeContext; -import org.sonar.core.issue.IssueUpdater; - -import java.util.Date; - -import static org.mockito.Matchers.argThat; -import static org.mockito.Mockito.*; - -public class IssueHandlersTest { - @Test - public void should_execute_handlers() throws Exception { - IssueHandler h1 = mock(IssueHandler.class); - IssueHandler h2 = mock(IssueHandler.class); - IssueUpdater updater = mock(IssueUpdater.class); - - IssueHandlers handlers = new IssueHandlers(updater, new IssueHandler[]{h1, h2}); - final DefaultIssue issue = new DefaultIssue(); - handlers.execute(issue, IssueChangeContext.createScan(new Date())); - - verify(h1).onIssue(argThat(new ArgumentMatcher<IssueHandler.Context>() { - @Override - public boolean matches(Object o) { - return ((IssueHandler.Context) o).issue() == issue; - } - })); - } - - @Test - public void test_no_handlers() { - IssueUpdater updater = mock(IssueUpdater.class); - IssueHandlers handlers = new IssueHandlers(updater); - handlers.execute(new DefaultIssue(), IssueChangeContext.createScan(new Date())); - verifyZeroInteractions(updater); - } -} diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/IssueTrackingDecoratorTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/IssueTrackingDecoratorTest.java deleted file mode 100644 index 337f16b94fe..00000000000 --- a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/IssueTrackingDecoratorTest.java +++ /dev/null @@ -1,574 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2014 SonarSource - * mailto:contact AT sonarsource DOT com - * - * SonarQube 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. - * - * SonarQube 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.plugins.core.issue; - -import org.apache.commons.codec.digest.DigestUtils; -import org.junit.Before; -import org.junit.Test; -import org.mockito.ArgumentCaptor; -import org.mockito.ArgumentMatcher; -import org.sonar.api.batch.DecoratorContext; -import org.sonar.api.batch.fs.internal.DefaultInputFile; -import org.sonar.api.component.ResourcePerspectives; -import org.sonar.api.issue.Issue; -import org.sonar.api.issue.internal.DefaultIssue; -import org.sonar.api.issue.internal.IssueChangeContext; -import org.sonar.api.profiles.RulesProfile; -import org.sonar.api.resources.File; -import org.sonar.api.resources.Project; -import org.sonar.api.resources.Resource; -import org.sonar.api.rule.RuleKey; -import org.sonar.api.rules.Rule; -import org.sonar.api.rules.RuleFinder; -import org.sonar.api.utils.Duration; -import org.sonar.api.utils.System2; -import org.sonar.batch.issue.IssueCache; -import org.sonar.batch.scan.LastLineHashes; -import org.sonar.batch.scan.filesystem.InputPathCache; -import org.sonar.core.issue.IssueUpdater; -import org.sonar.core.issue.db.IssueChangeDto; -import org.sonar.core.issue.db.IssueDto; -import org.sonar.core.issue.workflow.IssueWorkflow; -import org.sonar.java.api.JavaClass; - -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.List; - -import static com.google.common.collect.Lists.newArrayList; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyCollection; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.argThat; -import static org.mockito.Matchers.eq; -import static org.mockito.Matchers.isA; -import static org.mockito.Mockito.*; - -public class IssueTrackingDecoratorTest { - - IssueTrackingDecorator decorator; - IssueCache issueCache = mock(IssueCache.class, RETURNS_MOCKS); - InitialOpenIssuesStack initialOpenIssues = mock(InitialOpenIssuesStack.class); - IssueTracking tracking = mock(IssueTracking.class, RETURNS_MOCKS); - LastLineHashes lastSnapshots = mock(LastLineHashes.class); - IssueHandlers handlers = mock(IssueHandlers.class); - IssueWorkflow workflow = mock(IssueWorkflow.class); - IssueUpdater updater = mock(IssueUpdater.class); - ResourcePerspectives perspectives = mock(ResourcePerspectives.class); - RulesProfile profile = mock(RulesProfile.class); - RuleFinder ruleFinder = mock(RuleFinder.class); - InputPathCache inputPathCache = mock(InputPathCache.class); - - @Before - public void init() { - decorator = new IssueTrackingDecorator( - issueCache, - initialOpenIssues, - tracking, - lastSnapshots, - handlers, - workflow, - updater, - new Project("foo"), - perspectives, - profile, - ruleFinder, - inputPathCache); - } - - @Test - public void should_execute_on_project() { - Project project = mock(Project.class); - assertThat(decorator.shouldExecuteOnProject(project)).isTrue(); - } - - @Test - public void should_not_be_executed_on_classes_not_methods() throws Exception { - DecoratorContext context = mock(DecoratorContext.class); - decorator.decorate(JavaClass.create("org.foo.Bar"), context); - verifyZeroInteractions(context, issueCache, tracking, handlers, workflow); - } - - @Test - public void should_process_open_issues() throws Exception { - Resource file = File.create("Action.java").setEffectiveKey("struts:Action.java").setId(123); - final DefaultIssue issue = new DefaultIssue(); - - // INPUT : one issue, no open issues during previous scan, no filtering - when(issueCache.byComponent("struts:Action.java")).thenReturn(Arrays.asList(issue)); - List<IssueDto> dbIssues = Collections.emptyList(); - when(initialOpenIssues.selectAndRemoveIssues("struts:Action.java")).thenReturn(dbIssues); - when(inputPathCache.getFile("foo", "Action.java")).thenReturn(mock(DefaultInputFile.class)); - decorator.doDecorate(file); - - // Apply filters, track, apply transitions, notify extensions then update cache - verify(tracking).track(isA(SourceHashHolder.class), eq(dbIssues), argThat(new ArgumentMatcher<Collection<DefaultIssue>>() { - @Override - public boolean matches(Object o) { - List<DefaultIssue> issues = (List<DefaultIssue>) o; - return issues.size() == 1 && issues.get(0) == issue; - } - })); - verify(workflow).doAutomaticTransition(eq(issue), any(IssueChangeContext.class)); - verify(handlers).execute(eq(issue), any(IssueChangeContext.class)); - verify(issueCache).put(issue); - } - - @Test - public void should_register_unmatched_issues_as_end_of_life() throws Exception { - // "Unmatched" issues existed in previous scan but not in current one -> they have to be closed - Resource file = File.create("Action.java").setEffectiveKey("struts:Action.java").setId(123); - - // INPUT : one issue existing during previous scan - IssueDto unmatchedIssue = new IssueDto().setKee("ABCDE").setResolution(null).setStatus("OPEN").setRuleKey("squid", "AvoidCycle"); - - IssueTrackingResult trackingResult = new IssueTrackingResult(); - trackingResult.addUnmatched(unmatchedIssue); - - when(tracking.track(isA(SourceHashHolder.class), anyCollection(), anyCollection())).thenReturn(trackingResult); - when(inputPathCache.getFile("foo", "Action.java")).thenReturn(mock(DefaultInputFile.class)); - - decorator.doDecorate(file); - - verify(workflow, times(1)).doAutomaticTransition(any(DefaultIssue.class), any(IssueChangeContext.class)); - verify(handlers, times(1)).execute(any(DefaultIssue.class), any(IssueChangeContext.class)); - - ArgumentCaptor<DefaultIssue> argument = ArgumentCaptor.forClass(DefaultIssue.class); - verify(issueCache).put(argument.capture()); - - DefaultIssue issue = argument.getValue(); - assertThat(issue.key()).isEqualTo("ABCDE"); - assertThat(issue.isNew()).isFalse(); - assertThat(issue.isEndOfLife()).isTrue(); - } - - @Test - public void manual_issues_should_be_moved_if_matching_line_found() throws Exception { - // INPUT : one issue existing during previous scan - IssueDto unmatchedIssue = new IssueDto().setKee("ABCDE").setReporter("freddy").setLine(6).setStatus("OPEN").setRuleKey("manual", "Performance"); - when(ruleFinder.findByKey(RuleKey.of("manual", "Performance"))).thenReturn(new Rule("manual", "Performance")); - - IssueTrackingResult trackingResult = new IssueTrackingResult(); - trackingResult.addUnmatched(unmatchedIssue); - - String originalSource = "public interface Action {\n" - + " void method1();\n" - + " void method2();\n" - + " void method3();\n" - + " void method4();\n" - + " void method5();\n" // Original issue here - + "}"; - String newSource = "public interface Action {\n" - + " void method5();\n" // New issue here - + " void method1();\n" - + " void method2();\n" - + " void method3();\n" - + " void method4();\n" - + "}"; - Resource file = mockHashes(originalSource, newSource); - - when(tracking.track(isA(SourceHashHolder.class), anyCollection(), anyCollection())).thenReturn(trackingResult); - - decorator.doDecorate(file); - - verify(workflow, times(1)).doAutomaticTransition(any(DefaultIssue.class), any(IssueChangeContext.class)); - verify(handlers, times(1)).execute(any(DefaultIssue.class), any(IssueChangeContext.class)); - - ArgumentCaptor<DefaultIssue> argument = ArgumentCaptor.forClass(DefaultIssue.class); - verify(issueCache).put(argument.capture()); - - DefaultIssue issue = argument.getValue(); - assertThat(issue.line()).isEqualTo(2); - assertThat(issue.key()).isEqualTo("ABCDE"); - assertThat(issue.isNew()).isFalse(); - assertThat(issue.isEndOfLife()).isFalse(); - assertThat(issue.isOnDisabledRule()).isFalse(); - } - - private Resource mockHashes(String originalSource, String newSource) { - DefaultInputFile inputFile = mock(DefaultInputFile.class); - byte[][] hashes = computeHashes(newSource); - when(inputFile.lineHashes()).thenReturn(hashes); - when(inputFile.key()).thenReturn("foo:Action.java"); - when(inputPathCache.getFile("foo", "Action.java")).thenReturn(inputFile); - when(lastSnapshots.getLineHashes("foo:Action.java")).thenReturn(computeHexHashes(originalSource)); - Resource file = File.create("Action.java"); - return file; - } - - @Test - public void manual_issues_should_be_untouched_if_already_closed() throws Exception { - - // INPUT : one issue existing during previous scan - IssueDto unmatchedIssue = new IssueDto().setKee("ABCDE").setReporter("freddy").setLine(1).setStatus("CLOSED").setRuleKey("manual", "Performance"); - when(ruleFinder.findByKey(RuleKey.of("manual", "Performance"))).thenReturn(new Rule("manual", "Performance")); - - IssueTrackingResult trackingResult = new IssueTrackingResult(); - trackingResult.addUnmatched(unmatchedIssue); - - String originalSource = "public interface Action {}"; - Resource file = mockHashes(originalSource, originalSource); - - when(tracking.track(isA(SourceHashHolder.class), anyCollection(), anyCollection())).thenReturn(trackingResult); - - decorator.doDecorate(file); - - verify(workflow, times(1)).doAutomaticTransition(any(DefaultIssue.class), any(IssueChangeContext.class)); - verify(handlers, times(1)).execute(any(DefaultIssue.class), any(IssueChangeContext.class)); - - ArgumentCaptor<DefaultIssue> argument = ArgumentCaptor.forClass(DefaultIssue.class); - verify(issueCache).put(argument.capture()); - - DefaultIssue issue = argument.getValue(); - assertThat(issue.line()).isEqualTo(1); - assertThat(issue.key()).isEqualTo("ABCDE"); - assertThat(issue.isNew()).isFalse(); - assertThat(issue.isEndOfLife()).isFalse(); - assertThat(issue.isOnDisabledRule()).isFalse(); - assertThat(issue.status()).isEqualTo("CLOSED"); - } - - @Test - public void manual_issues_should_be_untouched_if_line_is_null() throws Exception { - - // INPUT : one issue existing during previous scan - IssueDto unmatchedIssue = new IssueDto().setKee("ABCDE").setReporter("freddy").setLine(null).setStatus("OPEN").setRuleKey("manual", "Performance"); - when(ruleFinder.findByKey(RuleKey.of("manual", "Performance"))).thenReturn(new Rule("manual", "Performance")); - - IssueTrackingResult trackingResult = new IssueTrackingResult(); - trackingResult.addUnmatched(unmatchedIssue); - - String originalSource = "public interface Action {}"; - Resource file = mockHashes(originalSource, originalSource); - - when(tracking.track(isA(SourceHashHolder.class), anyCollection(), anyCollection())).thenReturn(trackingResult); - - decorator.doDecorate(file); - - verify(workflow, times(1)).doAutomaticTransition(any(DefaultIssue.class), any(IssueChangeContext.class)); - verify(handlers, times(1)).execute(any(DefaultIssue.class), any(IssueChangeContext.class)); - - ArgumentCaptor<DefaultIssue> argument = ArgumentCaptor.forClass(DefaultIssue.class); - verify(issueCache).put(argument.capture()); - - DefaultIssue issue = argument.getValue(); - assertThat(issue.line()).isEqualTo(null); - assertThat(issue.key()).isEqualTo("ABCDE"); - assertThat(issue.isNew()).isFalse(); - assertThat(issue.isEndOfLife()).isFalse(); - assertThat(issue.isOnDisabledRule()).isFalse(); - assertThat(issue.status()).isEqualTo("OPEN"); - } - - @Test - public void manual_issues_should_be_kept_if_matching_line_not_found() throws Exception { - // "Unmatched" issues existed in previous scan but not in current one -> they have to be closed - - // INPUT : one issue existing during previous scan - final int issueOnLine = 6; - IssueDto unmatchedIssue = new IssueDto().setKee("ABCDE").setReporter("freddy").setLine(issueOnLine).setStatus("OPEN").setRuleKey("manual", "Performance"); - when(ruleFinder.findByKey(RuleKey.of("manual", "Performance"))).thenReturn(new Rule("manual", "Performance")); - - IssueTrackingResult trackingResult = new IssueTrackingResult(); - trackingResult.addUnmatched(unmatchedIssue); - - String originalSource = "public interface Action {\n" - + " void method1();\n" - + " void method2();\n" - + " void method3();\n" - + " void method4();\n" - + " void method5();\n" // Original issue here - + "}"; - String newSource = "public interface Action {\n" - + " void method1();\n" - + " void method2();\n" - + " void method3();\n" - + " void method4();\n" - + " void method6();\n" // Poof, no method5 anymore - + "}"; - - Resource file = mockHashes(originalSource, newSource); - - when(tracking.track(isA(SourceHashHolder.class), anyCollection(), anyCollection())).thenReturn(trackingResult); - - decorator.doDecorate(file); - - verify(workflow, times(1)).doAutomaticTransition(any(DefaultIssue.class), any(IssueChangeContext.class)); - verify(handlers, times(1)).execute(any(DefaultIssue.class), any(IssueChangeContext.class)); - - ArgumentCaptor<DefaultIssue> argument = ArgumentCaptor.forClass(DefaultIssue.class); - verify(issueCache).put(argument.capture()); - - DefaultIssue issue = argument.getValue(); - assertThat(issue.line()).isEqualTo(issueOnLine); - assertThat(issue.key()).isEqualTo("ABCDE"); - assertThat(issue.isNew()).isFalse(); - assertThat(issue.isEndOfLife()).isFalse(); - assertThat(issue.isOnDisabledRule()).isFalse(); - } - - @Test - public void manual_issues_should_be_kept_if_multiple_matching_lines_found() throws Exception { - // "Unmatched" issues existed in previous scan but not in current one -> they have to be closed - - // INPUT : one issue existing during previous scan - final int issueOnLine = 3; - IssueDto unmatchedIssue = new IssueDto().setKee("ABCDE").setReporter("freddy").setLine(issueOnLine).setStatus("OPEN").setRuleKey("manual", "Performance"); - when(ruleFinder.findByKey(RuleKey.of("manual", "Performance"))).thenReturn(new Rule("manual", "Performance")); - - IssueTrackingResult trackingResult = new IssueTrackingResult(); - trackingResult.addUnmatched(unmatchedIssue); - - String originalSource = "public class Action {\n" - + " void method1() {\n" - + " notify();\n" // initial issue - + " }\n" - + "}"; - String newSource = "public class Action {\n" - + " \n" - + " void method1() {\n" // new issue will appear here - + " notify();\n" - + " }\n" - + " void method2() {\n" - + " notify();\n" - + " }\n" - + "}"; - Resource file = mockHashes(originalSource, newSource); - - when(tracking.track(isA(SourceHashHolder.class), anyCollection(), anyCollection())).thenReturn(trackingResult); - - decorator.doDecorate(file); - - verify(workflow, times(1)).doAutomaticTransition(any(DefaultIssue.class), any(IssueChangeContext.class)); - verify(handlers, times(1)).execute(any(DefaultIssue.class), any(IssueChangeContext.class)); - - ArgumentCaptor<DefaultIssue> argument = ArgumentCaptor.forClass(DefaultIssue.class); - verify(issueCache).put(argument.capture()); - - DefaultIssue issue = argument.getValue(); - assertThat(issue.line()).isEqualTo(issueOnLine); - assertThat(issue.key()).isEqualTo("ABCDE"); - assertThat(issue.isNew()).isFalse(); - assertThat(issue.isEndOfLife()).isFalse(); - assertThat(issue.isOnDisabledRule()).isFalse(); - } - - @Test - public void manual_issues_should_be_closed_if_manual_rule_is_removed() throws Exception { - // "Unmatched" issues existed in previous scan but not in current one -> they have to be closed - - // INPUT : one issue existing during previous scan - IssueDto unmatchedIssue = new IssueDto().setKee("ABCDE").setReporter("freddy").setLine(1).setStatus("OPEN").setRuleKey("manual", "Performance"); - when(ruleFinder.findByKey(RuleKey.of("manual", "Performance"))).thenReturn(new Rule("manual", "Performance").setStatus(Rule.STATUS_REMOVED)); - - IssueTrackingResult trackingResult = new IssueTrackingResult(); - trackingResult.addUnmatched(unmatchedIssue); - - String source = "public interface Action {}"; - Resource file = mockHashes(source, source); - - when(tracking.track(isA(SourceHashHolder.class), anyCollection(), anyCollection())).thenReturn(trackingResult); - - decorator.doDecorate(file); - - verify(workflow, times(1)).doAutomaticTransition(any(DefaultIssue.class), any(IssueChangeContext.class)); - verify(handlers, times(1)).execute(any(DefaultIssue.class), any(IssueChangeContext.class)); - - ArgumentCaptor<DefaultIssue> argument = ArgumentCaptor.forClass(DefaultIssue.class); - verify(issueCache).put(argument.capture()); - - DefaultIssue issue = argument.getValue(); - assertThat(issue.key()).isEqualTo("ABCDE"); - assertThat(issue.isNew()).isFalse(); - assertThat(issue.isEndOfLife()).isTrue(); - assertThat(issue.isOnDisabledRule()).isTrue(); - } - - @Test - public void manual_issues_should_be_closed_if_manual_rule_is_not_found() throws Exception { - // "Unmatched" issues existed in previous scan but not in current one -> they have to be closed - - // INPUT : one issue existing during previous scan - IssueDto unmatchedIssue = new IssueDto().setKee("ABCDE").setReporter("freddy").setLine(1).setStatus("OPEN").setRuleKey("manual", "Performance"); - when(ruleFinder.findByKey(RuleKey.of("manual", "Performance"))).thenReturn(null); - - IssueTrackingResult trackingResult = new IssueTrackingResult(); - trackingResult.addUnmatched(unmatchedIssue); - - String source = "public interface Action {}"; - Resource file = mockHashes(source, source); - - when(tracking.track(isA(SourceHashHolder.class), anyCollection(), anyCollection())).thenReturn(trackingResult); - - decorator.doDecorate(file); - - verify(workflow, times(1)).doAutomaticTransition(any(DefaultIssue.class), any(IssueChangeContext.class)); - verify(handlers, times(1)).execute(any(DefaultIssue.class), any(IssueChangeContext.class)); - - ArgumentCaptor<DefaultIssue> argument = ArgumentCaptor.forClass(DefaultIssue.class); - verify(issueCache).put(argument.capture()); - - DefaultIssue issue = argument.getValue(); - assertThat(issue.key()).isEqualTo("ABCDE"); - assertThat(issue.isNew()).isFalse(); - assertThat(issue.isEndOfLife()).isTrue(); - assertThat(issue.isOnDisabledRule()).isTrue(); - } - - @Test - public void manual_issues_should_be_closed_if_new_source_is_shorter() throws Exception { - // "Unmatched" issues existed in previous scan but not in current one -> they have to be closed - - // INPUT : one issue existing during previous scan - IssueDto unmatchedIssue = new IssueDto().setKee("ABCDE").setReporter("freddy").setLine(6).setStatus("OPEN").setRuleKey("manual", "Performance"); - when(ruleFinder.findByKey(RuleKey.of("manual", "Performance"))).thenReturn(null); - - IssueTrackingResult trackingResult = new IssueTrackingResult(); - trackingResult.addUnmatched(unmatchedIssue); - - String originalSource = "public interface Action {\n" - + " void method1();\n" - + " void method2();\n" - + " void method3();\n" - + " void method4();\n" - + " void method5();\n" - + "}"; - String newSource = "public interface Action {\n" - + " void method1();\n" - + " void method2();\n" - + "}"; - Resource file = mockHashes(originalSource, newSource); - - when(tracking.track(isA(SourceHashHolder.class), anyCollection(), anyCollection())).thenReturn(trackingResult); - - decorator.doDecorate(file); - - verify(workflow, times(1)).doAutomaticTransition(any(DefaultIssue.class), any(IssueChangeContext.class)); - verify(handlers, times(1)).execute(any(DefaultIssue.class), any(IssueChangeContext.class)); - - ArgumentCaptor<DefaultIssue> argument = ArgumentCaptor.forClass(DefaultIssue.class); - verify(issueCache).put(argument.capture()); - - DefaultIssue issue = argument.getValue(); - verify(updater).setResolution(eq(issue), eq(Issue.RESOLUTION_REMOVED), any(IssueChangeContext.class)); - verify(updater).setStatus(eq(issue), eq(Issue.STATUS_CLOSED), any(IssueChangeContext.class)); - - assertThat(issue.key()).isEqualTo("ABCDE"); - assertThat(issue.isNew()).isFalse(); - assertThat(issue.isEndOfLife()).isTrue(); - assertThat(issue.isOnDisabledRule()).isTrue(); - } - - @Test - public void should_register_issues_on_deleted_components() throws Exception { - Project project = new Project("struts"); - DefaultIssue openIssue = new DefaultIssue(); - when(issueCache.byComponent("struts")).thenReturn(Arrays.asList(openIssue)); - IssueDto deadIssue = new IssueDto().setKee("ABCDE").setResolution(null).setStatus("OPEN").setRuleKey("squid", "AvoidCycle"); - when(initialOpenIssues.selectAllIssues()).thenReturn(Arrays.asList(deadIssue)); - - decorator.doDecorate(project); - - // the dead issue must be closed -> apply automatic transition, notify handlers and add to cache - verify(workflow, times(2)).doAutomaticTransition(any(DefaultIssue.class), any(IssueChangeContext.class)); - verify(handlers, times(2)).execute(any(DefaultIssue.class), any(IssueChangeContext.class)); - verify(issueCache, times(2)).put(any(DefaultIssue.class)); - - verify(issueCache).put(argThat(new ArgumentMatcher<DefaultIssue>() { - @Override - public boolean matches(Object o) { - DefaultIssue dead = (DefaultIssue) o; - return "ABCDE".equals(dead.key()) && !dead.isNew() && dead.isEndOfLife(); - } - })); - } - - @Test - public void merge_matched_issue() throws Exception { - IssueDto previousIssue = new IssueDto().setKee("ABCDE").setResolution(null).setStatus("OPEN").setRuleKey("squid", "AvoidCycle") - .setLine(10).setSeverity("MAJOR").setMessage("Message").setEffortToFix(1.5).setDebt(1L).setProjectKey("sample"); - DefaultIssue issue = new DefaultIssue(); - - IssueTrackingResult trackingResult = mock(IssueTrackingResult.class); - when(trackingResult.matched()).thenReturn(newArrayList(issue)); - when(trackingResult.matching(eq(issue))).thenReturn(previousIssue); - decorator.mergeMatched(trackingResult); - - verify(updater).setPastSeverity(eq(issue), eq("MAJOR"), any(IssueChangeContext.class)); - verify(updater).setPastLine(eq(issue), eq(10)); - verify(updater).setPastMessage(eq(issue), eq("Message"), any(IssueChangeContext.class)); - verify(updater).setPastEffortToFix(eq(issue), eq(1.5), any(IssueChangeContext.class)); - verify(updater).setPastTechnicalDebt(eq(issue), eq(Duration.create(1L)), any(IssueChangeContext.class)); - verify(updater).setPastProject(eq(issue), eq("sample"), any(IssueChangeContext.class)); - } - - @Test - public void merge_matched_issue_on_manual_severity() throws Exception { - IssueDto previousIssue = new IssueDto().setKee("ABCDE").setResolution(null).setStatus("OPEN").setRuleKey("squid", "AvoidCycle") - .setLine(10).setManualSeverity(true).setSeverity("MAJOR").setMessage("Message").setEffortToFix(1.5).setDebt(1L); - DefaultIssue issue = new DefaultIssue(); - - IssueTrackingResult trackingResult = mock(IssueTrackingResult.class); - when(trackingResult.matched()).thenReturn(newArrayList(issue)); - when(trackingResult.matching(eq(issue))).thenReturn(previousIssue); - decorator.mergeMatched(trackingResult); - - assertThat(issue.manualSeverity()).isTrue(); - assertThat(issue.severity()).isEqualTo("MAJOR"); - verify(updater, never()).setPastSeverity(eq(issue), anyString(), any(IssueChangeContext.class)); - } - - @Test - public void merge_issue_changelog_with_previous_changelog() throws Exception { - when(initialOpenIssues.selectChangelog("ABCDE")).thenReturn(newArrayList(new IssueChangeDto().setIssueKey("ABCD").setCreatedAt(System2.INSTANCE.now()))); - - IssueDto previousIssue = new IssueDto().setKee("ABCDE").setResolution(null).setStatus("OPEN").setRuleKey("squid", "AvoidCycle") - .setLine(10).setMessage("Message").setEffortToFix(1.5).setDebt(1L).setCreatedAt(System2.INSTANCE.now()); - DefaultIssue issue = new DefaultIssue(); - - IssueTrackingResult trackingResult = mock(IssueTrackingResult.class); - when(trackingResult.matched()).thenReturn(newArrayList(issue)); - when(trackingResult.matching(eq(issue))).thenReturn(previousIssue); - decorator.mergeMatched(trackingResult); - - assertThat(issue.changes()).hasSize(1); - } - - private byte[][] computeHashes(String source) { - String[] lines = source.split("\n"); - byte[][] hashes = new byte[lines.length][]; - for (int i = 0; i < lines.length; i++) { - hashes[i] = DigestUtils.md5(lines[i].replaceAll("[\t ]", "")); - } - return hashes; - } - - private String[] computeHexHashes(String source) { - String[] lines = source.split("\n"); - String[] hashes = new String[lines.length]; - for (int i = 0; i < lines.length; i++) { - hashes[i] = DigestUtils.md5Hex(lines[i].replaceAll("[\t ]", "")); - } - return hashes; - } - -} diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/IssueTrackingTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/IssueTrackingTest.java deleted file mode 100644 index 7fb8b6a9103..00000000000 --- a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/IssueTrackingTest.java +++ /dev/null @@ -1,369 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2014 SonarSource - * mailto:contact AT sonarsource DOT com - * - * SonarQube 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. - * - * SonarQube 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.plugins.core.issue; - -import com.google.common.base.Charsets; -import com.google.common.io.Resources; -import org.apache.commons.codec.digest.DigestUtils; -import org.junit.Before; -import org.junit.Test; -import org.sonar.api.batch.fs.internal.DefaultInputFile; -import org.sonar.api.issue.Issue; -import org.sonar.api.issue.internal.DefaultIssue; -import org.sonar.api.resources.Project; -import org.sonar.api.resources.Resource; -import org.sonar.api.rule.RuleKey; -import org.sonar.batch.scan.LastLineHashes; -import org.sonar.core.issue.db.IssueDto; - -import java.io.IOException; -import java.util.Arrays; -import java.util.Collections; - -import static com.google.common.collect.Lists.newArrayList; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verifyZeroInteractions; -import static org.mockito.Mockito.when; - -public class IssueTrackingTest { - - IssueTracking tracking; - Resource project; - SourceHashHolder sourceHashHolder; - LastLineHashes lastSnapshots; - long violationId = 0; - - @Before - public void before() { - lastSnapshots = mock(LastLineHashes.class); - - project = mock(Project.class); - tracking = new IssueTracking(); - } - - @Test - public void key_should_be_the_prioritary_field_to_check() { - IssueDto referenceIssue1 = newReferenceIssue("message", 10, "squid", "AvoidCycle", "checksum1").setKee("100"); - IssueDto referenceIssue2 = newReferenceIssue("message", 10, "squid", "AvoidCycle", "checksum2").setKee("200"); - - // exactly the fields of referenceIssue1 but not the same key - DefaultIssue newIssue = newDefaultIssue("message", 10, RuleKey.of("squid", "AvoidCycle"), "checksum1").setKey("200"); - - IssueTrackingResult result = new IssueTrackingResult(); - tracking.mapIssues(newArrayList(newIssue), newArrayList(referenceIssue1, referenceIssue2), null, result); - // same key - assertThat(result.matching(newIssue)).isSameAs(referenceIssue2); - } - - @Test - public void checksum_should_have_greater_priority_than_line() { - IssueDto referenceIssue1 = newReferenceIssue("message", 1, "squid", "AvoidCycle", "checksum1"); - IssueDto referenceIssue2 = newReferenceIssue("message", 3, "squid", "AvoidCycle", "checksum2"); - - DefaultIssue newIssue1 = newDefaultIssue("message", 3, RuleKey.of("squid", "AvoidCycle"), "checksum1"); - DefaultIssue newIssue2 = newDefaultIssue("message", 5, RuleKey.of("squid", "AvoidCycle"), "checksum2"); - - IssueTrackingResult result = new IssueTrackingResult(); - tracking.mapIssues(newArrayList(newIssue1, newIssue2), newArrayList(referenceIssue1, referenceIssue2), null, result); - assertThat(result.matching(newIssue1)).isSameAs(referenceIssue1); - assertThat(result.matching(newIssue2)).isSameAs(referenceIssue2); - } - - /** - * SONAR-2928 - */ - @Test - public void same_rule_and_null_line_and_checksum_but_different_messages() { - DefaultIssue newIssue = newDefaultIssue("new message", null, RuleKey.of("squid", "AvoidCycle"), "checksum1"); - IssueDto referenceIssue = newReferenceIssue("old message", null, "squid", "AvoidCycle", "checksum1"); - - IssueTrackingResult result = new IssueTrackingResult(); - tracking.mapIssues(newArrayList(newIssue), newArrayList(referenceIssue), null, result); - assertThat(result.matching(newIssue)).isSameAs(referenceIssue); - } - - @Test - public void same_rule_and_line_and_checksum_but_different_messages() { - DefaultIssue newIssue = newDefaultIssue("new message", 1, RuleKey.of("squid", "AvoidCycle"), "checksum1"); - IssueDto referenceIssue = newReferenceIssue("old message", 1, "squid", "AvoidCycle", "checksum1"); - - IssueTrackingResult result = new IssueTrackingResult(); - tracking.mapIssues(newArrayList(newIssue), newArrayList(referenceIssue), null, result); - assertThat(result.matching(newIssue)).isSameAs(referenceIssue); - } - - @Test - public void same_rule_and_line_message() { - DefaultIssue newIssue = newDefaultIssue("message", 1, RuleKey.of("squid", "AvoidCycle"), "checksum1"); - IssueDto referenceIssue = newReferenceIssue("message", 1, "squid", "AvoidCycle", "checksum2"); - - IssueTrackingResult result = new IssueTrackingResult(); - tracking.mapIssues(newArrayList(newIssue), newArrayList(referenceIssue), null, result); - assertThat(result.matching(newIssue)).isSameAs(referenceIssue); - } - - @Test - public void should_ignore_reference_measure_without_checksum() { - DefaultIssue newIssue = newDefaultIssue("message", 1, RuleKey.of("squid", "AvoidCycle"), null); - IssueDto referenceIssue = newReferenceIssue("message", 1, "squid", "NullDeref", null); - - IssueTrackingResult result = new IssueTrackingResult(); - tracking.mapIssues(newArrayList(newIssue), newArrayList(referenceIssue), null, result); - assertThat(result.matching(newIssue)).isNull(); - } - - @Test - public void same_rule_and_message_and_checksum_but_different_line() { - DefaultIssue newIssue = newDefaultIssue("message", 1, RuleKey.of("squid", "AvoidCycle"), "checksum1"); - IssueDto referenceIssue = newReferenceIssue("message", 2, "squid", "AvoidCycle", "checksum1"); - - IssueTrackingResult result = new IssueTrackingResult(); - tracking.mapIssues(newArrayList(newIssue), newArrayList(referenceIssue), null, result); - assertThat(result.matching(newIssue)).isSameAs(referenceIssue); - } - - /** - * SONAR-2812 - */ - @Test - public void same_checksum_and_rule_but_different_line_and_different_message() { - DefaultIssue newIssue = newDefaultIssue("new message", 1, RuleKey.of("squid", "AvoidCycle"), "checksum1"); - IssueDto referenceIssue = newReferenceIssue("old message", 2, "squid", "AvoidCycle", "checksum1"); - - IssueTrackingResult result = new IssueTrackingResult(); - tracking.mapIssues(newArrayList(newIssue), newArrayList(referenceIssue), null, result); - assertThat(result.matching(newIssue)).isSameAs(referenceIssue); - } - - @Test - public void should_create_new_issue_when_same_rule_same_message_but_different_line_and_checksum() { - DefaultIssue newIssue = newDefaultIssue("message", 1, RuleKey.of("squid", "AvoidCycle"), "checksum1"); - IssueDto referenceIssue = newReferenceIssue("message", 2, "squid", "AvoidCycle", "checksum2"); - - IssueTrackingResult result = new IssueTrackingResult(); - tracking.mapIssues(newArrayList(newIssue), newArrayList(referenceIssue), null, result); - assertThat(result.matching(newIssue)).isNull(); - } - - @Test - public void should_not_track_issue_if_different_rule() { - DefaultIssue newIssue = newDefaultIssue("message", 1, RuleKey.of("squid", "AvoidCycle"), "checksum1"); - IssueDto referenceIssue = newReferenceIssue("message", 1, "squid", "NullDeref", "checksum1"); - - IssueTrackingResult result = new IssueTrackingResult(); - tracking.mapIssues(newArrayList(newIssue), newArrayList(referenceIssue), null, result); - assertThat(result.matching(newIssue)).isNull(); - } - - @Test - public void should_compare_issues_with_database_format() { - // issue messages are trimmed and can be abbreviated when persisted in database. - // Comparing issue messages must use the same format. - DefaultIssue newIssue = newDefaultIssue(" message ", 1, RuleKey.of("squid", "AvoidCycle"), "checksum1"); - IssueDto referenceIssue = newReferenceIssue("message", 1, "squid", "AvoidCycle", "checksum2"); - - IssueTrackingResult result = new IssueTrackingResult(); - tracking.mapIssues(newArrayList(newIssue), newArrayList(referenceIssue), null, result); - assertThat(result.matching(newIssue)).isSameAs(referenceIssue); - } - - @Test - public void past_issue_not_associated_with_line_should_not_cause_npe() throws Exception { - initLastHashes("example2-v1", "example2-v2"); - - DefaultIssue newIssue = newDefaultIssue("Indentation", 9, RuleKey.of("squid", "AvoidCycle"), "foo"); - IssueDto referenceIssue = newReferenceIssue("2 branches need to be covered", null, "squid", "AvoidCycle", null); - - IssueTrackingResult result = tracking.track(sourceHashHolder, newArrayList(referenceIssue), newArrayList(newIssue)); - - assertThat(result.matched()).isEmpty(); - } - - @Test - public void new_issue_not_associated_with_line_should_not_cause_npe() throws Exception { - initLastHashes("example2-v1", "example2-v2"); - - DefaultIssue newIssue = newDefaultIssue("1 branch need to be covered", null, RuleKey.of("squid", "AvoidCycle"), "foo"); - IssueDto referenceIssue = newReferenceIssue("Indentationd", 7, "squid", "AvoidCycle", null); - - IssueTrackingResult result = tracking.track(sourceHashHolder, newArrayList(referenceIssue), newArrayList(newIssue)); - - assertThat(result.matched()).isEmpty(); - } - - /** - * SONAR-2928 - */ - @Test - public void issue_not_associated_with_line() throws Exception { - initLastHashes("example2-v1", "example2-v2"); - - DefaultIssue newIssue = newDefaultIssue("1 branch need to be covered", null, RuleKey.of("squid", "AvoidCycle"), null); - IssueDto referenceIssue = newReferenceIssue("2 branches need to be covered", null, "squid", "AvoidCycle", null); - - IssueTrackingResult result = tracking.track(sourceHashHolder, newArrayList(referenceIssue), newArrayList(newIssue)); - - assertThat(result.matching(newIssue)).isEqualTo(referenceIssue); - } - - /** - * SONAR-3072 - */ - @Test - public void should_track_issues_based_on_blocks_recognition_on_example1() throws Exception { - initLastHashes("example1-v1", "example1-v2"); - - IssueDto referenceIssue1 = newReferenceIssue("Indentation", 7, "squid", "AvoidCycle", null); - IssueDto referenceIssue2 = newReferenceIssue("Indentation", 11, "squid", "AvoidCycle", null); - - DefaultIssue newIssue1 = newDefaultIssue("Indentation", 9, RuleKey.of("squid", "AvoidCycle"), null); - DefaultIssue newIssue2 = newDefaultIssue("Indentation", 13, RuleKey.of("squid", "AvoidCycle"), null); - DefaultIssue newIssue3 = newDefaultIssue("Indentation", 17, RuleKey.of("squid", "AvoidCycle"), null); - DefaultIssue newIssue4 = newDefaultIssue("Indentation", 21, RuleKey.of("squid", "AvoidCycle"), null); - - IssueTrackingResult result = tracking.track(sourceHashHolder, Arrays.asList(referenceIssue1, referenceIssue2), Arrays.asList(newIssue1, newIssue2, newIssue3, newIssue4)); - - assertThat(result.matching(newIssue1)).isNull(); - assertThat(result.matching(newIssue2)).isNull(); - assertThat(result.matching(newIssue3)).isSameAs(referenceIssue1); - assertThat(result.matching(newIssue4)).isSameAs(referenceIssue2); - } - - /** - * SONAR-3072 - */ - @Test - public void should_track_issues_based_on_blocks_recognition_on_example2() throws Exception { - initLastHashes("example2-v1", "example2-v2"); - - IssueDto referenceIssue1 = newReferenceIssue("SystemPrintln", 5, "squid", "AvoidCycle", null); - - DefaultIssue newIssue1 = newDefaultIssue("SystemPrintln", 6, RuleKey.of("squid", "AvoidCycle"), null); - DefaultIssue newIssue2 = newDefaultIssue("SystemPrintln", 10, RuleKey.of("squid", "AvoidCycle"), null); - DefaultIssue newIssue3 = newDefaultIssue("SystemPrintln", 14, RuleKey.of("squid", "AvoidCycle"), null); - - IssueTrackingResult result = new IssueTrackingResult(); - tracking.mapIssues( - Arrays.asList(newIssue1, newIssue2, newIssue3), - Arrays.asList(referenceIssue1), - sourceHashHolder, result); - - assertThat(result.matching(newIssue1)).isNull(); - assertThat(result.matching(newIssue2)).isSameAs(referenceIssue1); - assertThat(result.matching(newIssue3)).isNull(); - } - - @Test - public void should_track_issues_based_on_blocks_recognition_on_example3() throws Exception { - initLastHashes("example3-v1", "example3-v2"); - - IssueDto referenceIssue1 = newReferenceIssue("Avoid unused local variables such as 'j'.", 6, "squid", "AvoidCycle", "63c11570fc0a76434156be5f8138fa03"); - IssueDto referenceIssue2 = newReferenceIssue("Avoid unused private methods such as 'myMethod()'.", 13, "squid", "NullDeref", "ef23288705d1ef1e512448ace287586e"); - IssueDto referenceIssue3 = newReferenceIssue("Method 'avoidUtilityClass' is not designed for extension - needs to be abstract, final or empty.", 9, "pmd", - "UnusedLocalVariable", "ed5cdd046fda82727d6fedd1d8e3a310"); - - // New issue - DefaultIssue newIssue1 = newDefaultIssue("Avoid unused local variables such as 'msg'.", 18, RuleKey.of("squid", "AvoidCycle"), "a24254126be2bf1a9b9a8db43f633733"); - // Same as referenceIssue2 - DefaultIssue newIssue2 = newDefaultIssue("Avoid unused private methods such as 'myMethod()'.", 13, RuleKey.of("squid", "NullDeref"), "ef23288705d1ef1e512448ace287586e"); - // Same as referenceIssue3 - DefaultIssue newIssue3 = newDefaultIssue("Method 'avoidUtilityClass' is not designed for extension - needs to be abstract, final or empty.", 9, - RuleKey.of("pmd", "UnusedLocalVariable"), "ed5cdd046fda82727d6fedd1d8e3a310"); - // New issue - DefaultIssue newIssue4 = newDefaultIssue("Method 'newViolation' is not designed for extension - needs to be abstract, final or empty.", 17, - RuleKey.of("pmd", "UnusedLocalVariable"), "7d58ac9040c27e4ca2f11a0269e251e2"); - // Same as referenceIssue1 - DefaultIssue newIssue5 = newDefaultIssue("Avoid unused local variables such as 'j'.", 6, RuleKey.of("squid", "AvoidCycle"), "4432a2675ec3e1620daefe38386b51ef"); - - IssueTrackingResult result = new IssueTrackingResult(); - tracking.mapIssues( - Arrays.asList(newIssue1, newIssue2, newIssue3, newIssue4, newIssue5), - Arrays.asList(referenceIssue1, referenceIssue2, referenceIssue3), - sourceHashHolder, result); - - assertThat(result.matching(newIssue1)).isNull(); - assertThat(result.matching(newIssue2)).isSameAs(referenceIssue2); - assertThat(result.matching(newIssue3)).isSameAs(referenceIssue3); - assertThat(result.matching(newIssue4)).isNull(); - assertThat(result.matching(newIssue5)).isSameAs(referenceIssue1); - } - - @Test - public void dont_load_checksum_if_no_new_issue() throws Exception { - sourceHashHolder = mock(SourceHashHolder.class); - - IssueDto referenceIssue = newReferenceIssue("2 branches need to be covered", null, "squid", "AvoidCycle", null); - - tracking.track(sourceHashHolder, newArrayList(referenceIssue), Collections.<DefaultIssue>emptyList()); - - verifyZeroInteractions(lastSnapshots, sourceHashHolder); - } - - private static String load(String name) throws IOException { - return Resources.toString(IssueTrackingTest.class.getResource("IssueTrackingTest/" + name + ".txt"), Charsets.UTF_8); - } - - private DefaultIssue newDefaultIssue(String message, Integer line, RuleKey ruleKey, String checksum) { - return new DefaultIssue().setMessage(message).setLine(line).setRuleKey(ruleKey).setChecksum(checksum).setStatus(Issue.STATUS_OPEN); - } - - private IssueDto newReferenceIssue(String message, Integer lineId, String ruleRepo, String ruleKey, String lineChecksum) { - IssueDto referenceIssue = new IssueDto(); - Long id = violationId++; - referenceIssue.setId(id); - referenceIssue.setKee(Long.toString(id)); - referenceIssue.setLine(lineId); - referenceIssue.setMessage(message); - referenceIssue.setRuleKey(ruleRepo, ruleKey); - referenceIssue.setChecksum(lineChecksum); - referenceIssue.setResolution(null); - referenceIssue.setStatus(Issue.STATUS_OPEN); - return referenceIssue; - } - - private void initLastHashes(String reference, String newSource) throws IOException { - DefaultInputFile inputFile = mock(DefaultInputFile.class); - byte[][] hashes = computeHashes(load(newSource)); - when(inputFile.lineHashes()).thenReturn(hashes); - when(inputFile.key()).thenReturn("foo:Action.java"); - when(lastSnapshots.getLineHashes("foo:Action.java")).thenReturn(computeHexHashes(load(reference))); - sourceHashHolder = new SourceHashHolder(inputFile, lastSnapshots); - } - - private byte[][] computeHashes(String source) { - String[] lines = source.split("\n"); - byte[][] hashes = new byte[lines.length][]; - for (int i = 0; i < lines.length; i++) { - hashes[i] = DigestUtils.md5(lines[i].replaceAll("[\t ]", "")); - } - return hashes; - } - - private String[] computeHexHashes(String source) { - String[] lines = source.split("\n"); - String[] hashes = new String[lines.length]; - for (int i = 0; i < lines.length; i++) { - hashes[i] = DigestUtils.md5Hex(lines[i].replaceAll("[\t ]", "")); - } - return hashes; - } -} diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/SourceHashHolderTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/SourceHashHolderTest.java deleted file mode 100644 index c9942c852b4..00000000000 --- a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/SourceHashHolderTest.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2014 SonarSource - * mailto:contact AT sonarsource DOT com - * - * SonarQube 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. - * - * SonarQube 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.plugins.core.issue; - -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mockito; -import org.sonar.api.batch.fs.InputFile; -import org.sonar.api.batch.fs.internal.DefaultInputFile; -import org.sonar.batch.scan.LastLineHashes; - -import static org.apache.commons.codec.digest.DigestUtils.md5; -import static org.apache.commons.codec.digest.DigestUtils.md5Hex; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -public class SourceHashHolderTest { - - SourceHashHolder sourceHashHolder; - - LastLineHashes lastSnapshots; - DefaultInputFile file; - - @Before - public void setUp() { - lastSnapshots = mock(LastLineHashes.class); - file = mock(DefaultInputFile.class); - - sourceHashHolder = new SourceHashHolder(file, lastSnapshots); - } - - @Test - public void should_lazy_load_line_hashes() { - final String source = "source"; - when(file.lineHashes()).thenReturn(new byte[][] {md5(source), null}); - - assertThat(sourceHashHolder.getHashedSource().getHash(1)).isEqualTo(md5Hex(source)); - assertThat(sourceHashHolder.getHashedSource().getHash(2)).isEqualTo(""); - verify(file).lineHashes(); - verify(file).key(); - verify(file).status(); - - assertThat(sourceHashHolder.getHashedSource().getHash(1)).isEqualTo(md5Hex(source)); - Mockito.verifyNoMoreInteractions(file); - } - - @Test - public void should_lazy_load_reference_hashes_when_status_changed() { - final String source = "source"; - String key = "foo:src/Foo.java"; - when(file.lineHashes()).thenReturn(new byte[][] {md5(source)}); - when(file.key()).thenReturn(key); - when(file.status()).thenReturn(InputFile.Status.CHANGED); - when(lastSnapshots.getLineHashes(key)).thenReturn(new String[] {md5Hex(source)}); - - assertThat(sourceHashHolder.getHashedReference().getHash(1)).isEqualTo(md5Hex(source)); - verify(lastSnapshots).getLineHashes(key); - - assertThat(sourceHashHolder.getHashedReference().getHash(1)).isEqualTo(md5Hex(source)); - Mockito.verifyNoMoreInteractions(lastSnapshots); - } - - @Test - public void should_not_load_reference_hashes_when_status_same() { - final String source = "source"; - String key = "foo:src/Foo.java"; - when(file.lineHashes()).thenReturn(new byte[][] {md5(source)}); - when(file.key()).thenReturn(key); - when(file.status()).thenReturn(InputFile.Status.SAME); - - assertThat(sourceHashHolder.getHashedReference().getHash(1)).isEqualTo(md5Hex(source)); - assertThat(sourceHashHolder.getHashedReference().getHash(1)).isEqualTo(md5Hex(source)); - Mockito.verifyNoMoreInteractions(lastSnapshots); - } - - @Test - public void no_reference_hashes_when_status_added() { - final String source = "source"; - String key = "foo:src/Foo.java"; - when(file.lineHashes()).thenReturn(new byte[][] {md5(source)}); - when(file.key()).thenReturn(key); - when(file.status()).thenReturn(InputFile.Status.ADDED); - - assertThat(sourceHashHolder.getHashedReference()).isNull(); - Mockito.verifyNoMoreInteractions(lastSnapshots); - } - -} diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/tracking/IssueTrackingBlocksRecognizerTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/tracking/IssueTrackingBlocksRecognizerTest.java deleted file mode 100644 index 55d84685253..00000000000 --- a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/tracking/IssueTrackingBlocksRecognizerTest.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2014 SonarSource - * mailto:contact AT sonarsource DOT com - * - * SonarQube 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. - * - * SonarQube 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.plugins.core.issue.tracking; - -import org.junit.Test; - -import static org.assertj.core.api.Assertions.assertThat; - -public class IssueTrackingBlocksRecognizerTest { - - @Test - public void test() { - assertThat(compute(t("abcde"), t("abcde"), 4, 4)).isEqualTo(5); - assertThat(compute(t("abcde"), t("abcd"), 4, 4)).isEqualTo(4); - assertThat(compute(t("bcde"), t("abcde"), 4, 4)).isEqualTo(0); - assertThat(compute(t("bcde"), t("abcde"), 3, 4)).isEqualTo(4); - } - - private static int compute(FileHashes a, FileHashes b, int ai, int bi) { - IssueTrackingBlocksRecognizer rec = new IssueTrackingBlocksRecognizer(a, b); - return rec.computeLengthOfMaximalBlock(ai, bi); - } - - private static FileHashes t(String text) { - String[] array = new String[text.length()]; - for (int i = 0; i < text.length(); i++) { - array[i] = "" + text.charAt(i); - } - return FileHashes.create(array); - } - -} diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/tracking/RollingFileHashesTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/tracking/RollingFileHashesTest.java deleted file mode 100644 index 50c3d0eefa0..00000000000 --- a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/tracking/RollingFileHashesTest.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2014 SonarSource - * mailto:contact AT sonarsource DOT com - * - * SonarQube 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. - * - * SonarQube 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.plugins.core.issue.tracking; - -import org.junit.Test; - -import static org.apache.commons.codec.digest.DigestUtils.md5Hex; -import static org.assertj.core.api.Assertions.assertThat; - -public class RollingFileHashesTest { - - @Test - public void test_equals() { - RollingFileHashes a = RollingFileHashes.create(FileHashes.create(new String[] {md5Hex("line0"), md5Hex("line1"), md5Hex("line2")}), 1); - RollingFileHashes b = RollingFileHashes.create(FileHashes.create(new String[] {md5Hex("line0"), md5Hex("line1"), md5Hex("line2"), md5Hex("line3")}), 1); - - assertThat(a.getHash(1) == b.getHash(1)).isTrue(); - assertThat(a.getHash(2) == b.getHash(2)).isTrue(); - assertThat(a.getHash(3) == b.getHash(3)).isFalse(); - - RollingFileHashes c = RollingFileHashes.create(FileHashes.create(new String[] {md5Hex("line-1"), md5Hex("line0"), md5Hex("line1"), md5Hex("line2"), md5Hex("line3")}), 1); - assertThat(a.getHash(1) == c.getHash(2)).isFalse(); - assertThat(a.getHash(2) == c.getHash(3)).isTrue(); - } - -} diff --git a/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/issue/IssueTrackingTest/example1-v1.txt b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/issue/IssueTrackingTest/example1-v1.txt deleted file mode 100644 index 1920333ddb6..00000000000 --- a/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/issue/IssueTrackingTest/example1-v1.txt +++ /dev/null @@ -1,12 +0,0 @@ -package example1; - -public class Toto { - - public void doSomething() { - // doSomething - } - - public void doSomethingElse() { - // doSomethingElse - } -} diff --git a/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/issue/IssueTrackingTest/example1-v2.txt b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/issue/IssueTrackingTest/example1-v2.txt deleted file mode 100644 index 231532452b2..00000000000 --- a/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/issue/IssueTrackingTest/example1-v2.txt +++ /dev/null @@ -1,22 +0,0 @@ -package example1; - -public class Toto { - - public Toto(){} - - public void doSomethingNew() { - // doSomethingNew - } - - public void doSomethingElseNew() { - // doSomethingElseNew - } - - public void doSomething() { - // doSomething - } - - public void doSomethingElse() { - // doSomethingElse - } -} diff --git a/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/issue/IssueTrackingTest/example2-v1.txt b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/issue/IssueTrackingTest/example2-v1.txt deleted file mode 100644 index a920afe459b..00000000000 --- a/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/issue/IssueTrackingTest/example2-v1.txt +++ /dev/null @@ -1,7 +0,0 @@ -package example2; - -public class Toto { - void method1() { - System.out.println("toto"); - } -} diff --git a/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/issue/IssueTrackingTest/example2-v2.txt b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/issue/IssueTrackingTest/example2-v2.txt deleted file mode 100644 index c5c8250cf65..00000000000 --- a/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/issue/IssueTrackingTest/example2-v2.txt +++ /dev/null @@ -1,16 +0,0 @@ -package example2; - -public class Toto { - - void method2() { - System.out.println("toto"); - } - - void method1() { - System.out.println("toto"); - } - - void method3() { - System.out.println("toto"); - } -} diff --git a/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/issue/IssueTrackingTest/example3-v1.txt b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/issue/IssueTrackingTest/example3-v1.txt deleted file mode 100644 index facdcbc008c..00000000000 --- a/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/issue/IssueTrackingTest/example3-v1.txt +++ /dev/null @@ -1,16 +0,0 @@ -package sample; - -public class Sample { - - public Sample(int i) { - int j = i+1; // violation: unused local variable - } - - public boolean avoidUtilityClass() { - return true; - } - - private String myMethod() { // violation : unused private method - return "hello"; - } -} diff --git a/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/issue/IssueTrackingTest/example3-v2.txt b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/issue/IssueTrackingTest/example3-v2.txt deleted file mode 100644 index 91db843fc4d..00000000000 --- a/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/issue/IssueTrackingTest/example3-v2.txt +++ /dev/null @@ -1,20 +0,0 @@ -package sample; - -public class Sample { - - public Sample(int i) { - int j = i+1; // still the same violation: unused local variable - } - - public boolean avoidUtilityClass() { - return true; - } - - private String myMethod() { // violation "unused private method" is fixed because it's called in newViolation - return "hello"; - } - - public void newViolation() { - String msg = myMethod(); // new violation : msg is an unused variable - } -} diff --git a/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/CreateIssueByInternalKeySensor.java b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/CreateIssueByInternalKeySensor.java index 886104c14a0..01973adef97 100644 --- a/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/CreateIssueByInternalKeySensor.java +++ b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/CreateIssueByInternalKeySensor.java @@ -19,7 +19,10 @@ */ package org.sonar.xoo.rule; +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.batch.fs.InputFile.Type; import org.sonar.api.batch.rule.ActiveRule; import org.sonar.api.batch.sensor.Sensor; import org.sonar.api.batch.sensor.SensorContext; @@ -40,7 +43,9 @@ public class CreateIssueByInternalKeySensor implements Sensor { @Override public void execute(SensorContext context) { - for (InputFile file : context.fileSystem().inputFiles(context.fileSystem().predicates().hasLanguages(Xoo.KEY))) { + FileSystem fs = context.fileSystem(); + FilePredicates p = fs.predicates(); + for (InputFile file : fs.inputFiles(p.and(p.hasLanguages(Xoo.KEY), p.hasType(Type.MAIN)))) { createIssues(file, context); } } diff --git a/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/OneIssuePerLineSensor.java b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/OneIssuePerLineSensor.java index 3e0b9f48ee5..7e79e2857f6 100644 --- a/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/OneIssuePerLineSensor.java +++ b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/OneIssuePerLineSensor.java @@ -19,7 +19,10 @@ */ package org.sonar.xoo.rule; +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.batch.fs.InputFile.Type; import org.sonar.api.batch.sensor.Sensor; import org.sonar.api.batch.sensor.SensorContext; import org.sonar.api.batch.sensor.SensorDescriptor; @@ -43,7 +46,9 @@ public class OneIssuePerLineSensor implements Sensor { @Override public void execute(SensorContext context) { - for (InputFile file : context.fileSystem().inputFiles(context.fileSystem().predicates().hasLanguages(Xoo.KEY))) { + FileSystem fs = context.fileSystem(); + FilePredicates p = fs.predicates(); + for (InputFile file : fs.inputFiles(p.and(p.hasLanguages(Xoo.KEY), p.hasType(Type.MAIN)))) { createIssues(file, context); } } |