public class IssueTrackingDecoratorTest extends AbstractDaoTestCase {
IssueTrackingDecorator decorator;
- IssueCache issueCache = mock(IssueCache.class);
+ IssueCache issueCache = mock(IssueCache.class, RETURNS_MOCKS);
InitialOpenIssuesStack initialOpenIssues = mock(InitialOpenIssuesStack.class);
IssueTracking tracking = mock(IssueTracking.class, RETURNS_MOCKS);
IssueHandlers handlers = mock(IssueHandlers.class);
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
- <version>2.2.2</version>
+ <version>2.2.4</version>
</dependency>
<dependency>
<groupId>com.akiban</groupId>
*/
package org.sonar.batch.index;
-import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.persistit.Exchange;
import com.persistit.Key;
import javax.annotation.CheckForNull;
import java.io.Serializable;
-import java.util.Collection;
import java.util.Iterator;
-import java.util.List;
import java.util.Set;
/**
public class Cache<K, V extends Serializable> {
private static final String DEFAULT_GROUP = "_";
- // TODO improve exception messages by using this cache name
private final String name;
private final Exchange exchange;
exchange.store();
return this;
} catch (Exception e) {
- throw new IllegalStateException("Fail to put element in cache", e);
+ throw new IllegalStateException("Fail to put element in the cache " + name, e);
}
}
}
return (V) exchange.getValue().get();
} catch (Exception e) {
- throw new IllegalStateException("Fail to get element from cache", e);
+ throw new IllegalStateException("Fail to get element from cache " + name, e);
}
}
exchange.append(group).append(key);
return exchange.remove();
} catch (Exception e) {
- throw new IllegalStateException("Fail to get element from cache", e);
+ throw new IllegalStateException("Fail to get element from cache " + name, e);
}
}
exchange.removeKeyRange(exchange.getKey(), key);
return this;
} catch (Exception e) {
- throw new IllegalStateException("Fail to clear cache group: " + group, e);
+ throw new IllegalStateException("Fail to clear group '" + group + "' from cache " + name, e);
}
}
}
return keys;
} catch (Exception e) {
- throw new IllegalStateException("Fail to get cache keys", e);
+ throw new IllegalStateException("Fail to get keys from cache " + name, e);
}
}
return keySet(DEFAULT_GROUP);
}
- // TODO implement a lazy-loading equivalent with Iterator/Iterable
- public Collection<V> values(String group) {
+ /**
+ * Lazy-loading values for a given group
+ */
+ public Iterable<V> values(String group) {
try {
- List<V> values = Lists.newArrayList();
exchange.clear();
Exchange iteratorExchange = new Exchange(exchange);
iteratorExchange.append(group).append(Key.BEFORE);
- while (iteratorExchange.next(false)) {
- if (iteratorExchange.getValue().isDefined()) {
- values.add((V) iteratorExchange.getValue().get());
- }
- }
- return values;
+ return new ValueIterable<V>(iteratorExchange, false);
} catch (Exception e) {
- throw new IllegalStateException("Fail to get cache values", e);
+ throw new IllegalStateException("Fail to get values from cache " + name, e);
}
}
+ /**
+ * Lazy-loading values
+ */
public Iterable<V> values() {
return values(DEFAULT_GROUP);
}
- public Collection<V> allValues() {
+ /**
+ * Lazy-loading values of all groups
+ */
+ public Iterable<V> allValues() {
try {
- List<V> values = Lists.newArrayList();
exchange.clear();
Exchange iteratorExchange = new Exchange(exchange);
iteratorExchange.append(Key.BEFORE);
- while (iteratorExchange.next(true)) {
- values.add((V) iteratorExchange.getValue().get());
- }
- return values;
+ return new ValueIterable<V>(iteratorExchange, true);
} catch (Exception e) {
- throw new IllegalStateException("Fail to get cache values", e);
+ throw new IllegalStateException("Fail to get values from cache " + name, e);
}
}
}
return groups;
} catch (Exception e) {
- throw new IllegalStateException("Fail to get cache values", e);
+ throw new IllegalStateException("Fail to get values from cache " + name, e);
}
}
return new EntryIterable(new Exchange(exchange), false);
}
+
+ //
+ // LAZY ITERATORS AND ITERABLES
+ //
+
+ private static class ValueIterable<T extends Serializable> implements Iterable<T> {
+ private final Iterator<T> iterator;
+
+ private ValueIterable(Exchange exchange, boolean deep) {
+ this.iterator = new ValueIterator<T>(exchange, deep);
+ }
+
+ @Override
+ public Iterator<T> iterator() {
+ return iterator;
+ }
+ }
+
+ private static class ValueIterator<T extends Serializable> implements Iterator<T> {
+ private final Exchange exchange;
+ private final boolean deep;
+
+ private ValueIterator(Exchange exchange, boolean deep) {
+ this.exchange = exchange;
+ this.deep = deep;
+ }
+
+ @Override
+ public boolean hasNext() {
+ try {
+ return exchange.next(deep);
+ } catch (PersistitException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ @Override
+ public T next() {
+ T value = null;
+ if (exchange.getValue().isDefined()) {
+ value = (T) exchange.getValue().get();
+ }
+ return value;
+ }
+
+ @Override
+ public void remove() {
+ }
+ }
+
private static class EntryIterable<T extends Serializable> implements Iterable<Entry<T>> {
private final EntryIterator<T> it;
*/
package org.sonar.batch.issue;
+import com.google.common.collect.Lists;
import org.sonar.api.component.Component;
import org.sonar.api.issue.Issuable;
import org.sonar.api.issue.Issue;
import org.sonar.core.issue.DefaultIssue;
import org.sonar.core.issue.DefaultIssueBuilder;
-import java.util.Collection;
+import java.util.List;
/**
* @since 3.6
@Override
public boolean addIssue(Issue issue) {
- return scanIssues.initAndAddIssue(((DefaultIssue)issue));
+ return scanIssues.initAndAddIssue(((DefaultIssue) issue));
}
@SuppressWarnings("unchecked")
@Override
- public Collection<Issue> issues() {
- return (Collection)cache.byComponent(component.key());
+ public List<Issue> issues() {
+ Iterable<DefaultIssue> elements = cache.byComponent(component.key());
+ return Lists.<Issue>newArrayList(elements);
}
@Override
}
public List<Violation> get(String componentKey) {
- Collection<DefaultIssue> issues = issueCache.byComponent(componentKey);
+ Iterable<DefaultIssue> issues = issueCache.byComponent(componentKey);
List<Violation> violations = Lists.newArrayList();
for (DefaultIssue issue : issues) {
violations.add(toViolation(issue));
import org.sonar.batch.index.Caches;
import org.sonar.core.issue.DefaultIssue;
-import java.util.Collection;
-
-import static com.google.common.collect.Lists.newArrayList;
-
/**
* Shared issues among all project modules
*/
cache = caches.createCache("issues");
}
- public Collection<DefaultIssue> byComponent(String componentKey) {
+ public Iterable<DefaultIssue> byComponent(String componentKey) {
return cache.values(componentKey);
}
- public Collection<DefaultIssue> all() {
- return newArrayList(cache.allValues());
+ public Iterable<DefaultIssue> all() {
+ return cache.allValues();
}
public IssueCache put(DefaultIssue issue) {
}
@VisibleForTesting
- void writeJson(Collection<Resource> resources, Writer output) {
+ void writeJson(Collection<Resource> resources, Writer writer) {
JsonWriter json = null;
try {
- json = new JsonWriter(output);
+ json = new JsonWriter(writer);
json.setSerializeNulls(false);
json.beginObject()
.beginObject();
for (Resource resource : resources) {
- Collection<DefaultIssue> issues = getIssues(resource);
- if (issues.isEmpty()) {
- continue;
- }
-
- json.name(resource.getKey())
- .beginArray();
-
+ Iterable<DefaultIssue> issues = getIssues(resource);
+ boolean hasViolation = false;
for (DefaultIssue issue : issues) {
- json.beginObject()
+ if (!hasViolation) {
+ json.name(resource.getKey()).beginArray();
+ hasViolation = true;
+ }
+ json
+ .beginObject()
.name("line").value(issue.line())
.name("message").value(issue.message())
.name("severity").value(issue.severity())
.name("rule_repository").value(issue.ruleKey().repository())
.name("rule_name").value(ruleName(issue.ruleKey()))
.name("switched_off").value(Issue.RESOLUTION_FALSE_POSITIVE.equals(issue.resolution()))
- .name("is_new").value((issue.isNew()));
- if (issue.creationDate() != null) {
- json.name("created_at").value(DateUtils.formatDateTime(issue.creationDate()));
- }
- json.endObject();
+ .name("is_new").value((issue.isNew()))
+ .name("created_at").value(DateUtils.formatDateTime(issue.creationDate()))
+ .endObject();
+ }
+ if (hasViolation) {
+ json.endArray();
}
-
- json.endArray();
}
- json.endObject()
- .endObject()
- .flush();
+ json.endObject().endObject().flush();
} catch (IOException e) {
throw new SonarException("Unable to export results", e);
} finally {
}
@VisibleForTesting
- Collection<DefaultIssue> getIssues(Resource resource) {
+ Iterable<DefaultIssue> getIssues(Resource resource) {
return issueCache.byComponent(resource.getEffectiveKey());
}
}
}
@VisibleForTesting
- Collection<DefaultIssue> getIssues() {
+ Iterable<DefaultIssue> getIssues() {
return issueCache.all();
}
}
cache.put("org/apache/struts/Filter.java", "lines", 500f);
assertThat(cache.values("org/apache/struts/Action.java")).containsOnly(123f, 200f);
assertThat(cache.values("org/apache/struts/Filter.java")).containsOnly(500f);
+ assertThat(cache.values("other")).isEmpty();
}
@Test
package org.sonar.batch.issue;
import org.junit.Test;
+import org.mockito.Mockito;
import org.sonar.api.component.Component;
import org.sonar.api.issue.Issuable;
import org.sonar.api.resources.File;
public class IssuableFactoryTest {
- ScanIssues issueFactory = mock(ScanIssues.class);
- IssueCache cache = mock(IssueCache.class);
+ ScanIssues scanIssues = mock(ScanIssues.class);
+ IssueCache cache = mock(IssueCache.class, Mockito.RETURNS_MOCKS);
@Test
public void file_should_be_issuable() throws Exception {
- IssuableFactory factory = new IssuableFactory(issueFactory, cache);
- Component component = new ResourceComponent(new File("foo/bar.c"));
+ IssuableFactory factory = new IssuableFactory(scanIssues, cache);
+ Component component = new ResourceComponent(new File("foo/bar.c").setEffectiveKey("foo/bar.c"));
Issuable issuable = factory.loadPerspective(Issuable.class, component);
assertThat(issuable).isNotNull();
@Test
public void project_should_be_issuable() throws Exception {
- IssuableFactory factory = new IssuableFactory(issueFactory, cache);
- Component component = new ResourceComponent(new Project("Foo"));
+ IssuableFactory factory = new IssuableFactory(scanIssues, cache);
+ Component component = new ResourceComponent(new Project("Foo").setEffectiveKey("foo"));
Issuable issuable = factory.loadPerspective(Issuable.class, component);
assertThat(issuable).isNotNull();
@Test
public void java_file_should_be_issuable() throws Exception {
- IssuableFactory factory = new IssuableFactory(issueFactory, cache);
- Component component = new ResourceComponent(new JavaFile("bar.Foo"));
+ IssuableFactory factory = new IssuableFactory(scanIssues, cache);
+ Component component = new ResourceComponent(new JavaFile("org.apache.Action").setEffectiveKey("struts:org.apache.Action"));
Issuable issuable = factory.loadPerspective(Issuable.class, component);
assertThat(issuable).isNotNull();
@Test
public void java_class_should_not_be_issuable() throws Exception {
- IssuableFactory factory = new IssuableFactory(issueFactory, cache);
- Component component = new ResourceComponent(JavaClass.create("bar", "Foo"));
+ IssuableFactory factory = new IssuableFactory(scanIssues, cache);
+ Component component = new ResourceComponent(JavaClass.create("org.apache.Action").setEffectiveKey("struts:org.apache.Action"));
Issuable issuable = factory.loadPerspective(Issuable.class, component);
assertThat(issuable).isNull();
import com.google.common.base.Function;
import com.google.common.collect.Collections2;
+import com.google.common.collect.ImmutableList;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import javax.annotation.Nullable;
import java.util.Collection;
+import java.util.List;
import static org.fest.assertions.Assertions.assertThat;
issue.setSeverity(Severity.MINOR);
cache.put(issue);
- Collection<DefaultIssue> issues = cache.byComponent("org.struts.Action");
+ List<DefaultIssue> issues = ImmutableList.copyOf(cache.byComponent("org.struts.Action"));
assertThat(issues).hasSize(1);
Issue reloaded = issues.iterator().next();
assertThat(reloaded.key()).isEqualTo("111");
DefaultIssue issue2 = new DefaultIssue().setKey("222").setComponentKey("org.struts.Filter").setSeverity(Severity.INFO);
cache.put(issue1).put(issue2);
- Collection<DefaultIssue> issues = cache.all();
- assertThat(issues).hasSize(2).containsOnly(issue1, issue2);
+ List<DefaultIssue> issues = ImmutableList.copyOf(cache.all());
+ assertThat(issues).containsOnly(issue1, issue2);
}
- private Collection<String> issueKeys(Collection<DefaultIssue> issues) {
- return Collections2.transform(issues, new Function<DefaultIssue, String>() {
+ private Collection<String> issueKeys(Iterable<DefaultIssue> issues) {
+ return Collections2.transform(ImmutableList.copyOf(issues), new Function<DefaultIssue, String>() {
@Override
public String apply(@Nullable DefaultIssue issue) {
return issue.key();
import org.sonar.api.component.Component;
import org.sonar.api.resources.File;
import org.sonar.api.resources.Project;
+import org.sonar.api.resources.Resource;
import org.sonar.api.source.Highlightable;
import org.sonar.batch.index.ComponentDataCache;
import org.sonar.core.component.ResourceComponent;
@Test
public void should_load_default_perspective() throws Exception {
- Component component = new ResourceComponent(new File("foo/bar.c"));
+ Resource file = new File("foo.c").setEffectiveKey("myproject:path/to/foo.c");
+ Component component = new ResourceComponent(file);
HighlightableBuilder builder = new HighlightableBuilder(cache);
Highlightable perspective = builder.loadPerspective(Highlightable.class, component);
@Test
public void project_should_not_be_highlightable() {
- Component component = new ResourceComponent(new Project("Foo"));
+ Component component = new ResourceComponent(new Project("struts").setEffectiveKey("org.struts"));
HighlightableBuilder builder = new HighlightableBuilder(cache);
Highlightable perspective = builder.loadPerspective(Highlightable.class, component);
@Test
public void java_class_should_not_be_highlightable() {
- Component component = new ResourceComponent(JavaClass.create("foo", "Bar"));
+ Component component = new ResourceComponent(JavaClass.create("org.struts.Action").setEffectiveKey("struts:org.struts.Action"));
HighlightableBuilder builder = new HighlightableBuilder(cache);
Highlightable perspective = builder.loadPerspective(Highlightable.class, component);
*/
package org.sonar.core.component;
+import com.google.common.base.Strings;
import org.sonar.api.component.Component;
import org.sonar.api.database.model.Snapshot;
import org.sonar.api.resources.Resource;
public ResourceComponent(Resource resource, @Nullable Snapshot snapshot) {
this.key = resource.getEffectiveKey();
+ if (Strings.isNullOrEmpty(key)) {
+ throw new IllegalArgumentException("Missing component key");
+ }
this.name = resource.getName();
this.longName = resource.getLongName();
this.qualifier = resource.getQualifier();
import org.junit.Test;
import org.sonar.api.database.model.Snapshot;
import org.sonar.api.resources.File;
+import org.sonar.api.resources.Resource;
import static org.fest.assertions.Assertions.assertThat;
+import static org.fest.assertions.Fail.fail;
public class ResourceComponentTest {
+
+ Resource file = new File("foo.c").setEffectiveKey("myproject:path/to/foo.c");
+
@Test
public void db_ids_should_be_optional() {
- ResourceComponent component = new ResourceComponent(new File("foo.c"), new Snapshot());
+ ResourceComponent component = new ResourceComponent(file, new Snapshot());
assertThat(component.snapshotId()).isNull();
assertThat(component.resourceId()).isNull();
Snapshot snapshot = new Snapshot();
snapshot.setId(123);
snapshot.setResourceId(456);
- ResourceComponent component = new ResourceComponent(new File("foo.c"), snapshot);
+ ResourceComponent component = new ResourceComponent(file, snapshot);
assertThat(component.snapshotId()).isEqualTo(123);
assertThat(component.resourceId()).isEqualTo(456);
@Test
public void should_use_effective_key() {
- File file = new File("foo.c");
- file.setEffectiveKey("myproject:path/to/foo.c");
ResourceComponent component = new ResourceComponent(file);
-
assertThat(component.key()).isEqualTo("myproject:path/to/foo.c");
}
+
+ @Test
+ public void effective_key_should_be_set() {
+ try {
+ File file = new File("foo.c");
+ new ResourceComponent(file);
+ fail();
+ } catch (IllegalArgumentException e) {
+ assertThat(e).hasMessage("Missing component key");
+ }
+ }
}
import org.sonar.api.rule.RuleKey;
import javax.annotation.Nullable;
-import java.util.Collection;
+import java.util.List;
/**
* @since 3.6
*/
boolean addIssue(Issue issue);
- Collection<Issue> issues();
+ List<Issue> issues();
}