/* * $Id$ * * Janus platform is an open-source multiagent platform. * More details on http://www.janusproject.io * * Copyright (C) 2014-2015 Sebastian RODRIGUEZ, Nicolas GAUD, Stéphane GALLAND. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package io.janusproject.tests.kernel.services.guava; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; import java.io.Serializable; import java.util.AbstractSet; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.UUID; import com.google.common.base.Supplier; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Multimap; import com.google.common.collect.Multimaps; import com.google.common.collect.Multiset; import com.google.common.collect.SetMultimap; import com.google.common.collect.Sets; import com.google.common.collect.testing.features.CollectionFeature; import com.google.common.collect.testing.features.CollectionSize; import com.google.common.collect.testing.features.MapFeature; import com.google.common.collect.testing.google.MultimapTestSuiteBuilder; import com.google.common.collect.testing.google.TestStringSetMultimapGenerator; import io.janusproject.kernel.services.guava.DMultiMapView; import io.janusproject.services.distributeddata.DMapListener; import io.janusproject.tests.testutils.AbstractJanusTest; import io.janusproject.util.DataViewDelegate; import io.janusproject.util.DataViewDelegate.Delegator; import junit.framework.TestSuite; import org.junit.Assume; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Suite; import org.junit.runners.Suite.SuiteClasses; import org.mockito.ArgumentCaptor; import io.sarl.tests.api.Nullable; /** * @author $Author: sgalland$ * @version $FullVersion$ * @mavengroupid $GroupId$ * @mavenartifactid $ArtifactId$ */ @RunWith(Suite.class) @SuiteClasses({ DMultiMapViewTest.BackedCollectionTests.class, DMultiMapViewTest.ViewTests.class, DMultiMapViewTest.SpecificDMultiMapFunctionTests.class, DMultiMapViewTest.ListeningFeatureTests.class, //DMultiMapViewTest.GuavaMultiMapOperationTests.class, }) @SuppressWarnings("all") public class DMultiMapViewTest { /** * @author $Author: sgalland$ * @version $FullVersion$ * @mavengroupid $GroupId$ * @mavenartifactid $ArtifactId$ */ public static class BackedCollectionTests extends AbstractJanusTest { @Nullable private String name; @Nullable private Multimap<String, String> map; @Nullable private DMultiMapView<String, String> view; @Before public void setUp() { this.name = UUID.randomUUID().toString(); Supplier<List<String>> supplier = new Supplier<List<String>>() { @Override public List<String> get() { return Lists.newArrayList(); } }; this.map = Multimaps.newListMultimap(Maps.<String, Collection<String>> newHashMap(), supplier); this.view = new DMultiMapView<>(this.name, this.map); this.view.put("a", "va"); this.view.put("b", "vb"); } @Test public void isBackedCollection() { assertTrue(this.view.isBackedCollection()); } @Test public void changesPropagation() { Assume.assumeTrue("The collection is not backing the changes to the underlying collection", this.view.isBackedCollection()); assertTrue(this.view.keySet().remove("b")); assertEquals(1, this.map.size()); assertTrue(this.map.containsKey("a")); assertTrue(this.map.containsValue("va")); } @Test public void noChangesPropagation() { Assume.assumeFalse("The collection is backing the changes to the underlying collection", this.view.isBackedCollection()); assertTrue(this.view.keySet().remove("b")); assertEquals(2, this.map.size()); assertTrue(this.map.containsKey("a")); assertTrue(this.map.containsValue("va")); assertTrue(this.map.containsKey("b")); assertTrue(this.map.containsValue("vb")); } } /** * @author $Author: sgalland$ * @version $FullVersion$ * @mavengroupid $GroupId$ * @mavenartifactid $ArtifactId$ */ public static class GuavaMultiMapOperationTests { public static TestSuite suite() { return MultimapTestSuiteBuilder // The create method is called with an array of elements // that should populate the collection. .using(new TestStringSetMultimapGenerator() { @Override protected SetMultimap<String, String> create(Entry<String, String>[] entries) { Map<String, Collection<String>> jvmMap = Maps.newHashMap(); Multimap<String, String> map = Multimaps.newSetMultimap(jvmMap, new Supplier<Set<String>>() { @Override public Set<String> get() { return Sets.newHashSet(); } }); for (Entry<String, String> entry : entries) { map.put(entry.getKey(), entry.getValue()); } DMultiMapView<String, String> view = new DMultiMapView<>(UUID.randomUUID().toString(), map); return new GuavaTestFakeView(view); } }).named("Guava-based DMultiMap tests") .withFeatures(MapFeature.ALLOWS_NULL_KEYS, MapFeature.ALLOWS_NULL_VALUES, MapFeature.ALLOWS_ANY_NULL_QUERIES, MapFeature.GENERAL_PURPOSE, MapFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION, CollectionFeature.SUPPORTS_ITERATOR_REMOVE, CollectionSize.ANY) .createTestSuite(); } } /** * @author $Author: sgalland$ * @version $FullVersion$ * @mavengroupid $GroupId$ * @mavenartifactid $ArtifactId$ */ public static class ViewTests extends AbstractJanusTest { @Nullable private Map<String, Collection<String>> jvmMap; @Nullable private Multimap<String, String> map; @Nullable private DMultiMapView<String, String> view; @Before public void setUp() { this.jvmMap = Maps.newHashMap(); this.map = Multimaps.newListMultimap(this.jvmMap, new Supplier<List<String>>() { @Override public List<String> get() { return Lists.newArrayList(); } }); this.view = new DMultiMapView<>(UUID.randomUUID().toString(), this.map); } @Test public void put() { String newKey = UUID.randomUUID().toString(); String newValue = UUID.randomUUID().toString(); this.map.put(newKey, newValue); // assertEquals(1, this.view.size()); Collection<String> col = this.view.get(newKey); assertNotNull(col); assertEquals(1, col.size()); assertSame(newValue, col.iterator().next()); } } /** * @author $Author: sgalland$ * @version $FullVersion$ * @mavengroupid $GroupId$ * @mavenartifactid $ArtifactId$ */ public static class SpecificDMultiMapFunctionTests extends AbstractJanusTest { @Nullable private Map<String, Collection<String>> jvmMap; @Nullable private Multimap<String, String> map; @Nullable private DMultiMapView<String, String> view; @Before public void setUp() { this.jvmMap = Maps.newHashMap(); this.map = Multimaps.newListMultimap(this.jvmMap, new Supplier<List<String>>() { @Override public List<String> get() { return Lists.newArrayList(); } }); this.view = new DMultiMapView<>(UUID.randomUUID().toString(), this.map); } @Test public void isBackedCollection() { assertTrue(this.view.isBackedCollection()); } @Test public void valueCount_empty() { assertEquals(0, this.view.valueCount("abc")); } @Test public void valueCount_oneKey_oneValue() { this.view.put("abc", "vABC"); assertEquals(1, this.view.valueCount("abc")); } @Test public void valueCount_oneKey_twoValues() { this.view.put("abc", "vABC"); this.view.put("abc", "vABC2"); assertEquals(2, this.view.valueCount("abc")); } @Test public void valueCount_oneKey_threeValues() { this.view.put("abc", "vABC"); this.view.put("abc", "vABC2"); this.view.put("abc", "vABC3"); assertEquals(3, this.view.valueCount("abc")); } @Test public void valueCount_twoKeys_oneValue() { this.view.put("abc", "vABC"); this.view.put("def", "vDEF"); assertEquals(1, this.view.valueCount("abc")); } @Test public void valueCount_twoKeys_twoValues() { this.view.put("abc", "vABC"); this.view.put("abc", "vABC2"); this.view.put("def", "vDEF"); this.view.put("def", "vDEF2"); assertEquals(2, this.view.valueCount("abc")); } @Test public void valueCount_twoKeys_threeValues() { this.view.put("abc", "vABC"); this.view.put("abc", "vABC2"); this.view.put("abc", "vABC2"); this.view.put("def", "vDEF"); this.view.put("def", "vDEF2"); this.view.put("def", "vDEF2"); assertEquals(3, this.view.valueCount("abc")); } } /** * @author $Author: sgalland$ * @version $FullVersion$ * @mavengroupid $GroupId$ * @mavenartifactid $ArtifactId$ */ public static class ListeningFeatureTests extends AbstractJanusTest { private DMapListener<String, String> listener; private Map<String, Collection<String>> jvmMap; private Multimap<String, String> map; private DMultiMapView<String, String> view; @Before public void setUp() { this.listener = mock(DMapListener.class); this.jvmMap = Maps.newHashMap(); this.map = Multimaps.newListMultimap(this.jvmMap, new Supplier<List<String>>() { @Override public List<String> get() { return Lists.newArrayList(); } }); this.view = new DMultiMapView<>(UUID.randomUUID().toString(), this.map); } @Test public void entryAdded_withoutListener() { this.view.put("abc", "vABC"); verifyZeroInteractions(this.listener); } @Test public void entryAdded_removeListener() { this.view.addDMapListener(this.listener); this.view.removeDMapListener(this.listener); this.view.put("abc", "vABC"); verifyZeroInteractions(this.listener); } @Test public void entryAdded_withListener() { this.view.addDMapListener(this.listener); this.view.put("abc", "vABC"); ArgumentCaptor<String> arg0 = ArgumentCaptor.forClass(String.class); ArgumentCaptor<String> arg1 = ArgumentCaptor.forClass(String.class); verify(this.listener, times(1)).entryAdded(arg0.capture(), arg1.capture()); assertEquals("abc", arg0.getValue()); assertEquals("vABC", arg1.getValue()); } @Test public void entryUpdated_withoutListener() { this.view.put("abc", "vABC"); this.view.put("abc", "vABC2"); verifyZeroInteractions(this.listener); } @Test public void entryUpdated_removeListener() { this.view.put("abc", "vABC"); this.view.addDMapListener(this.listener); this.view.removeDMapListener(this.listener); this.view.put("abc", "vABC2"); verifyZeroInteractions(this.listener); } @Test public void entryUpdated_withListener() { this.view.put("abc", "vABC"); this.view.addDMapListener(this.listener); this.view.put("abc", "vABC2"); ArgumentCaptor<String> arg0 = ArgumentCaptor.forClass(String.class); ArgumentCaptor<String> arg1 = ArgumentCaptor.forClass(String.class); verify(this.listener, times(1)).entryAdded(arg0.capture(), arg1.capture()); assertEquals("abc", arg0.getValue()); assertEquals("vABC2", arg1.getValue()); } @Test public void entryRemoved_withListener() { this.view.put("abc", "vABC"); this.view.removeAll("abc"); verifyZeroInteractions(this.listener); } @Test public void entryRemoved_removeListener() { this.view.put("abc", "vABC"); this.view.addDMapListener(this.listener); this.view.removeDMapListener(this.listener); this.view.removeAll("abc"); verifyZeroInteractions(this.listener); } @Test public void entryRemoved_withoutListener() { this.view.put("abc", "vABC"); this.view.addDMapListener(this.listener); this.view.removeAll("abc"); ArgumentCaptor<String> arg0 = ArgumentCaptor.forClass(String.class); ArgumentCaptor<String> arg1 = ArgumentCaptor.forClass(String.class); verify(this.listener, times(1)).entryRemoved(arg0.capture(), arg1.capture()); assertEquals("abc", arg0.getValue()); assertEquals("vABC", arg1.getValue()); } @Test public void mapCleared_withListener() { this.view.put("abc", "vABC"); this.view.clear(); verifyZeroInteractions(this.listener); } @Test public void mapCleared_removeListener() { this.view.put("abc", "vABC"); this.view.addDMapListener(this.listener); this.view.removeDMapListener(this.listener); this.view.clear(); verifyZeroInteractions(this.listener); } @Test public void mapCleared_withoutListener() { this.view.put("abc", "vABC"); this.view.addDMapListener(this.listener); this.view.clear(); ArgumentCaptor<Boolean> arg0 = ArgumentCaptor.forClass(Boolean.class); verify(this.listener, times(1)).mapCleared(arg0.capture()); assertFalse(arg0.getValue()); } } /** * @author $Author: sgalland$ * @version $FullVersion$ * @mavengroupid $GroupId$ * @mavenartifactid $ArtifactId$ * @param <K> * @param <V> */ private static class GuavaTestFakeView implements SetMultimap<String, String>, Serializable, Delegator<DMultiMapView<String, String>> { private static final long serialVersionUID = -6970650402150118406L; private final DMultiMapView<String, String> dmultimap; public GuavaTestFakeView(DMultiMapView<String, String> dmultimap) { if (!(DataViewDelegate.undelegate(dmultimap) instanceof SetMultimap<?, ?>)) { throw new AssertionError("Incompatible type of multimap. It must be a SetMultimap."); } this.dmultimap = dmultimap; } /** * {@inheritDoc} */ @Override public DMultiMapView<String, String> getDelegatedObject() { return this.dmultimap; } @Override public String toString() { return getDelegatedObject().toString(); } @Override public boolean equals(Object obj) { return getDelegatedObject().equals(DataViewDelegate.undelegate(obj)); } @Override public int hashCode() { return getDelegatedObject().hashCode(); } @Override public int size() { return this.dmultimap.size(); } @Override public boolean isEmpty() { return this.dmultimap.isEmpty(); } @Override public boolean containsKey(Object key) { return this.dmultimap.containsKey(key); } @Override public boolean containsValue(Object value) { return this.dmultimap.containsValue(value); } @Override public boolean containsEntry(Object key, Object value) { return this.dmultimap.containsEntry(key, value); } @Override public boolean put(String key, String value) { return this.dmultimap.put(key, value); } @Override public boolean remove(Object key, Object value) { return this.dmultimap.remove(key, value); } @Override public boolean putAll(String key, Iterable<? extends String> values) { return this.dmultimap.putAll(key, values); } @Override public boolean putAll(Multimap<? extends String, ? extends String> multimap) { return this.dmultimap.putAll(multimap); } @Override public void clear() { this.dmultimap.clear(); } @Override public Set<String> keySet() { return this.dmultimap.keySet(); } @Override public Multiset<String> keys() { return this.dmultimap.keys(); } @Override public Collection<String> values() { return this.dmultimap.values(); } @Override public Map<String, Collection<String>> asMap() { return this.dmultimap.asMap(); } @Override public Set<String> get(String key) { return new SetView(this.dmultimap.get(key)); } @Override public Set<String> removeAll(Object key) { return new SetView(this.dmultimap.removeAll(key)); } @Override public Set<String> replaceValues(String key, Iterable<? extends String> values) { return new SetView<>(this.dmultimap.replaceValues(key, values)); } @Override public Set<Entry<String, String>> entries() { return new SetView(this.dmultimap.entries()); } /** * @author $Author: sgalland$ * @version $FullVersion$ * @mavengroupid $GroupId$ * @mavenartifactid $ArtifactId$ */ private static class SetView<E> extends AbstractSet<E> implements Serializable { private static final long serialVersionUID = -6970650402150118406L; private final Collection<E> collection; public SetView(Collection<E> collection) { this.collection = collection; } @Override public String toString() { return this.collection.toString(); } @Override public boolean equals(Object o) { return this.collection.equals(o); } @Override public int hashCode() { return this.collection.hashCode(); } @Override public boolean add(E e) { return this.collection.add(e); } @Override public void clear() { this.collection.clear(); } @Override public Iterator<E> iterator() { return this.collection.iterator(); } @Override public int size() { return this.collection.size(); } } } }