package org.infinispan.functional; import static org.infinispan.commons.api.functional.EntryVersion.CompareResult.EQUAL; import static org.infinispan.commons.marshall.MarshallableFunctions.identity; import static org.infinispan.commons.marshall.MarshallableFunctions.removeReturnPrevOrNull; import static org.infinispan.commons.marshall.MarshallableFunctions.returnReadOnlyFindOrNull; import static org.infinispan.commons.marshall.MarshallableFunctions.returnReadWriteFind; import static org.infinispan.commons.marshall.MarshallableFunctions.returnReadWriteGet; import static org.infinispan.commons.marshall.MarshallableFunctions.returnReadWriteView; import static org.infinispan.commons.marshall.MarshallableFunctions.setValueConsumer; import static org.infinispan.commons.marshall.MarshallableFunctions.setValueReturnPrevOrNull; import static org.infinispan.commons.marshall.MarshallableFunctions.setValueReturnView; import static org.infinispan.functional.FunctionalTestUtils.assertReadOnlyViewEmpty; import static org.infinispan.functional.FunctionalTestUtils.assertReadOnlyViewEquals; import static org.infinispan.functional.FunctionalTestUtils.assertReadWriteViewEmpty; import static org.infinispan.functional.FunctionalTestUtils.assertReadWriteViewEquals; import static org.infinispan.functional.FunctionalTestUtils.await; import static org.infinispan.functional.FunctionalTestUtils.ro; import static org.infinispan.functional.FunctionalTestUtils.rw; import static org.infinispan.functional.FunctionalTestUtils.supplyIntKey; import static org.infinispan.functional.FunctionalTestUtils.wo; import static org.infinispan.test.TestingUtil.withCacheManager; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertFalse; import static org.testng.AssertJUnit.assertNotNull; import static org.testng.AssertJUnit.assertNull; import static org.testng.AssertJUnit.assertTrue; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; import java.io.Serializable; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.function.BiConsumer; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Supplier; import org.infinispan.AdvancedCache; import org.infinispan.commons.api.functional.EntryVersion.NumericEntryVersion; import org.infinispan.commons.api.functional.EntryView.ReadEntryView; import org.infinispan.commons.api.functional.EntryView.ReadWriteEntryView; import org.infinispan.commons.api.functional.EntryView.WriteEntryView; import org.infinispan.commons.api.functional.FunctionalMap.ReadOnlyMap; import org.infinispan.commons.api.functional.FunctionalMap.ReadWriteMap; import org.infinispan.commons.api.functional.FunctionalMap.WriteOnlyMap; import org.infinispan.commons.api.functional.MetaParam.MetaEntryVersion; import org.infinispan.commons.api.functional.MetaParam.MetaLifespan; import org.infinispan.commons.api.functional.Traversable; import org.infinispan.commons.marshall.Externalizer; import org.infinispan.commons.marshall.SerializeFunctionWith; import org.infinispan.commons.marshall.SerializeWith; import org.infinispan.functional.impl.FunctionalMapImpl; import org.infinispan.functional.impl.ReadOnlyMapImpl; import org.infinispan.functional.impl.ReadWriteMapImpl; import org.infinispan.functional.impl.WriteOnlyMapImpl; import org.infinispan.test.CacheManagerCallable; import org.infinispan.test.fwk.TestCacheManagerFactory; import org.testng.annotations.Test; /** * Test suite for verifying basic functional map functionality, * and for testing out functionality that is not available via standard * {@link java.util.concurrent.ConcurrentMap} nor JSR-107 JCache * APIs, such as atomic conditional metadata-based replace operations, which * are required by Hot Rod. */ @Test(groups = "functional", testName = "functional.FunctionalMapTest") public class FunctionalMapTest extends AbstractFunctionalTest { public void testLocalWriteConstantAndReadGetsValue() { doWriteConstantAndReadGetsValue(supplyIntKey(), ro(fmapL1), wo(fmapL2)); } public void testReplWriteConstantAndReadGetsValueOnNonOwner() { doWriteConstantAndReadGetsValue(supplyKeyForCache(0, REPL), ro(fmapR1), wo(fmapR2)); } public void testReplWriteConstantAndReadGetsValueOnOwner() { doWriteConstantAndReadGetsValue(supplyKeyForCache(1, REPL), ro(fmapR1), wo(fmapR2)); } public void testDistWriteConstantAndReadGetsValueOnNonOwner() { doWriteConstantAndReadGetsValue(supplyKeyForCache(0, DIST), ro(fmapD1), wo(fmapD2)); } public void testDistWriteConstantAndReadGetsValueOnOwner() { doWriteConstantAndReadGetsValue(supplyKeyForCache(1, DIST), ro(fmapD1), wo(fmapD2)); } /** * Write-only allows for constant, non-capturing, values to be written, * and read-only allows for those values to be retrieved. */ private <K> void doWriteConstantAndReadGetsValue(Supplier<K> keySupplier, ReadOnlyMap<K, String> map1, WriteOnlyMap<K, String> map2) { K key = keySupplier.get(); await( map2.eval(key, SetStringConstant.INSTANCE).thenCompose(r -> map1.eval(key, returnReadOnlyFindOrNull()).thenAccept(v -> { assertNull(r); assertEquals("one", v); } ) ) ); } @SerializeWith(value = SetStringConstant.Externalizer0.class) private static final class SetStringConstant implements Consumer<WriteEntryView<String>> { @Override public void accept(WriteEntryView<String> wo) { wo.set("one"); } private static final SetStringConstant INSTANCE = new SetStringConstant(); public static final class Externalizer0 implements Externalizer<Object> { public void writeObject(ObjectOutput oo, Object o) {} public Object readObject(ObjectInput input) { return INSTANCE; } } } public void testLocalWriteValueAndReadValueAndMetadata() { doWriteValueAndReadValueAndMetadata(supplyIntKey(), ro(fmapL1), wo(fmapL2)); } public void testReplWriteValueAndReadValueAndMetadataOnNonOwner() { doWriteValueAndReadValueAndMetadata(supplyKeyForCache(0, REPL), ro(fmapR1), wo(fmapR2)); } public void testReplWriteValueAndReadValueAndMetadataOnOwner() { doWriteValueAndReadValueAndMetadata(supplyKeyForCache(1, REPL), ro(fmapR1), wo(fmapR2)); } public void testDistWriteValueAndReadValueAndMetadataOnNonOwner() { doWriteValueAndReadValueAndMetadata(supplyKeyForCache(0, DIST), ro(fmapD1), wo(fmapD2)); } public void testDistWriteValueAndReadValueAndMetadataOnOwner() { doWriteValueAndReadValueAndMetadata(supplyKeyForCache(1, DIST), ro(fmapD1), wo(fmapD2)); } /** * Write-only allows for non-capturing values to be written along with metadata, * and read-only allows for both values and metadata to be retrieved. */ private <K> void doWriteValueAndReadValueAndMetadata(Supplier<K> keySupplier, ReadOnlyMap<K, String> map1, WriteOnlyMap<K, String> map2) { K key = keySupplier.get(); await( map2.eval(key, "one", SetValueAndConstantLifespan.getInstance()).thenCompose(r -> map1.eval(key, identity()).thenAccept(ro -> { assertNull(r); assertEquals(Optional.of("one"), ro.find()); assertEquals("one", ro.get()); assertEquals(Optional.of(new MetaLifespan(100000)), ro.findMetaParam(MetaLifespan.class)); } ) ) ); } @SerializeWith(value = SetValueAndConstantLifespan.Externalizer0.class) private static final class SetValueAndConstantLifespan<V> implements BiConsumer<V, WriteEntryView<V>> { @Override public void accept(V v, WriteEntryView<V> wo) { wo.set(v, new MetaLifespan(100000)); } @SuppressWarnings("unchecked") private static <V> SetValueAndConstantLifespan<V> getInstance() { return INSTANCE; } private static final SetValueAndConstantLifespan INSTANCE = new SetValueAndConstantLifespan<>(); public static final class Externalizer0 implements Externalizer<Object> { public void writeObject(ObjectOutput oo, Object o) {} public Object readObject(ObjectInput input) { return INSTANCE; } } } public void testLocalReadWriteGetsEmpty() { doReadWriteGetsEmpty(supplyIntKey(), rw(fmapL1)); } public void testReplReadWriteGetsEmptyOnNonOwner() { doReadWriteGetsEmpty(supplyKeyForCache(0, REPL), rw(fmapR1)); } public void testReplReadWriteGetsEmptyOnOwner() { doReadWriteGetsEmpty(supplyKeyForCache(1, REPL), rw(fmapR1)); } public void testDistReadWriteGetsEmptyOnNonOwner() { doReadWriteGetsEmpty(supplyKeyForCache(0, DIST), rw(fmapD1)); } public void testDistReadWriteGetsEmptyOnOwner() { doReadWriteGetsEmpty(supplyKeyForCache(1, DIST), rw(fmapD1)); } /** * Read-write allows to retrieve an empty cache entry. */ private <K> void doReadWriteGetsEmpty(Supplier<K> keySupplier, ReadWriteMap<K, String> map) { K key = keySupplier.get(); await(map.eval(key, returnReadWriteFind()).thenAccept(v -> assertEquals(Optional.empty(), v))); } public void testLocalReadWriteValuesReturnPrevious() { doReadWriteConstantReturnPrev(supplyIntKey(), rw(fmapL1), rw(fmapL2)); } public void testReplReadWriteValuesReturnPreviousOnNonOwner() { doReadWriteConstantReturnPrev(supplyKeyForCache(0, REPL), rw(fmapR1), rw(fmapR2)); } public void testReplReadWriteValuesReturnPreviousOnOwner() { doReadWriteConstantReturnPrev(supplyKeyForCache(1, REPL), rw(fmapR1), rw(fmapR2)); } public void testDistReadWriteValuesReturnPreviousOnNonOwner() { doReadWriteConstantReturnPrev(supplyKeyForCache(0, DIST), rw(fmapD1), rw(fmapD2)); } public void testDistReadWriteValuesReturnPreviousOnOwner() { doReadWriteConstantReturnPrev(supplyKeyForCache(1, DIST), rw(fmapD1), rw(fmapD2)); } /** * Read-write allows for constant, non-capturing, values to be written, * returns previous value, and also allows values to be retrieved. */ private <K> void doReadWriteConstantReturnPrev(Supplier<K> keySupplier, ReadWriteMap<K, String> map1, ReadWriteMap<K, String> map2) { K key = keySupplier.get(); await( map2.eval(key, SetStringConstantReturnPrevious.getInstance()).thenCompose(r -> map1.eval(key, returnReadWriteGet()).thenAccept(v -> { assertFalse(r.isPresent()); assertEquals("one", v); } ) ) ); } @SerializeFunctionWith(value = SetStringConstantReturnPrevious.Externalizer0.class) private static final class SetStringConstantReturnPrevious<K> implements Function<ReadWriteEntryView<K, String>, Optional<String>> { @Override public Optional<String> apply(ReadWriteEntryView<K, String> rw) { Optional<String> prev = rw.find(); rw.set("one"); return prev; } @SuppressWarnings("unchecked") private static <K> SetStringConstantReturnPrevious<K> getInstance() { return INSTANCE; } private static final SetStringConstantReturnPrevious INSTANCE = new SetStringConstantReturnPrevious<>(); public static final class Externalizer0 implements Externalizer<Object> { public void writeObject(ObjectOutput oo, Object o) {} public Object readObject(ObjectInput input) { return INSTANCE; } } } public void testLocalReadWriteForConditionalParamBasedReplace() { doReadWriteForConditionalParamBasedReplace(supplyIntKey(), rw(fmapL1), rw(fmapL2)); } public void testReplReadWriteForConditionalParamBasedReplaceOnNonOwner() { doReadWriteForConditionalParamBasedReplace(supplyKeyForCache(0, REPL), rw(fmapR1), rw(fmapR2)); } public void testReplReadWriteForConditionalParamBasedReplaceOnOwner() { doReadWriteForConditionalParamBasedReplace(supplyKeyForCache(1, REPL), rw(fmapR1), rw(fmapR2)); } public void testDistReadWriteForConditionalParamBasedReplaceOnNonOwner() { doReadWriteForConditionalParamBasedReplace(supplyKeyForCache(0, DIST), rw(fmapD1), rw(fmapD2)); } public void testDistReadWriteForConditionalParamBasedReplaceOnOwner() { doReadWriteForConditionalParamBasedReplace(supplyKeyForCache(1, DIST), rw(fmapD1), rw(fmapD2)); } /** * Read-write allows for replace operations to happen based on version * comparison, and update version information if replace is versions are * equals. * * This is the kind of advance operation that Hot Rod prototocol requires * but the current Infinispan API is unable to offer without offering * atomicity at the level of the function that compares the version * information. */ private <K> void doReadWriteForConditionalParamBasedReplace(Supplier<K> keySupplier, ReadWriteMap<K, String> map1, ReadWriteMap<K, String> map2) { replaceWithVersion(keySupplier, map1, map2, 100, rw -> { assertEquals("uno", rw.get()); assertEquals(Optional.of(new MetaEntryVersion<>(new NumericEntryVersion(200))), rw.findMetaParam(MetaEntryVersion.class)); } ); replaceWithVersion(keySupplier, map1, map2, 900, rw -> { assertEquals(Optional.of("one"), rw.find()); assertEquals(Optional.of(new MetaEntryVersion<>(new NumericEntryVersion(100))), rw.findMetaParam(MetaEntryVersion.class)); }); } private <K> void replaceWithVersion(Supplier<K> keySupplier, ReadWriteMap<K, String> map1, ReadWriteMap<K, String> map2, long version, Consumer<ReadWriteEntryView<K, String>> asserts) { K key = keySupplier.get(); await( map1.eval(key, SetStringAndVersionConstant.getInstance()).thenCompose(r -> map2.eval(key, new VersionBasedConditionalReplace<>(version)).thenAccept(rw -> { assertNull(r); asserts.accept(rw); } ) ) ); } @SerializeFunctionWith(value = SetStringAndVersionConstant.Externalizer0.class) private static final class SetStringAndVersionConstant<K> implements Function<ReadWriteEntryView<K, String>, Void> { @Override public Void apply(ReadWriteEntryView<K, String> rw) { rw.set("one", new MetaEntryVersion<>(new NumericEntryVersion(100))); return null; } @SuppressWarnings("unchecked") private static <K> SetStringAndVersionConstant<K> getInstance() { return INSTANCE; } private static final SetStringAndVersionConstant INSTANCE = new SetStringAndVersionConstant<>(); public static final class Externalizer0 implements Externalizer<Object> { public void writeObject(ObjectOutput oo, Object o) {} public Object readObject(ObjectInput input) { return INSTANCE; } } } @SerializeFunctionWith(value = VersionBasedConditionalReplace.Externalizer0.class) private static final class VersionBasedConditionalReplace<K> implements Function<ReadWriteEntryView<K, String>, ReadWriteEntryView<K, String>> { private final long version; private VersionBasedConditionalReplace(long version) { this.version = version; } @Override public ReadWriteEntryView<K, String> apply(ReadWriteEntryView<K, String> rw) { Class<MetaEntryVersion<Long>> clazz = MetaEntryVersion.type(); Optional<MetaEntryVersion<Long>> metaParam = rw.findMetaParam(clazz); metaParam.ifPresent(metaVersion -> { if (metaVersion.get().compareTo(new NumericEntryVersion(version)) == EQUAL) rw.set("uno", new MetaEntryVersion<>(new NumericEntryVersion(200))); }); return rw; } public static final class Externalizer0 implements Externalizer<VersionBasedConditionalReplace<?>> { @Override public void writeObject(ObjectOutput output, VersionBasedConditionalReplace<?> object) throws IOException { output.writeLong(object.version); } @Override public VersionBasedConditionalReplace<?> readObject(ObjectInput input) throws IOException, ClassNotFoundException { long version = input.readLong(); return new VersionBasedConditionalReplace<>(version); } } } public void testAutoClose() throws Exception { withCacheManager(new CacheManagerCallable(TestCacheManagerFactory.createCacheManager()) { @Override public void call() throws Exception { cm.defineConfiguration("read-only", cm.getDefaultCacheConfiguration()); AdvancedCache<?, ?> readOnlyCache = cm.getCache("read-only").getAdvancedCache(); try (ReadOnlyMap<?, ?> ro = ReadOnlyMapImpl.create(FunctionalMapImpl.create(readOnlyCache))) { assertNotNull(ro); // No-op, just verify that it implements AutoCloseable } assertTrue(readOnlyCache.getStatus().isTerminated()); cm.defineConfiguration("write-only", cm.getDefaultCacheConfiguration()); AdvancedCache<?, ?> writeOnlyCache = cm.getCache("write-only").getAdvancedCache(); try (WriteOnlyMap<?, ?> wo = WriteOnlyMapImpl.create(FunctionalMapImpl.create(writeOnlyCache))) { assertNotNull(wo); // No-op, just verify that it implements AutoCloseable } assertTrue(writeOnlyCache.getStatus().isTerminated()); cm.defineConfiguration("read-write", cm.getDefaultCacheConfiguration()); AdvancedCache<?, ?> readWriteCache = cm.getCache("read-write").getAdvancedCache(); try (ReadWriteMap<?, ?> rw = ReadWriteMapImpl.create(FunctionalMapImpl.create(readWriteCache))) { assertNotNull(rw); // No-op, just verify that it implements AutoCloseable } assertTrue(readWriteCache.getStatus().isTerminated()); } }); } public void testLocalReadOnlyEvalManyEmpty() { doReadOnlyEvalManyEmpty(supplyIntKey(), ro(fmapL1)); } public void testReplReadOnlyEvalManyEmptyOnNonOwner() { doReadOnlyEvalManyEmpty(supplyKeyForCache(0, REPL), ro(fmapR1)); } public void testReplReadOnlyEvalManyEmptyOnOwner() { doReadOnlyEvalManyEmpty(supplyKeyForCache(1, REPL), ro(fmapR1)); } public void testDistReadOnlyEvalManyEmptyOnNonOwner() { doReadOnlyEvalManyEmpty(supplyKeyForCache(0, DIST), ro(fmapD1)); } public void testDistReadOnlyEvalManyEmptyOnOwner() { doReadOnlyEvalManyEmpty(supplyKeyForCache(1, DIST), ro(fmapD1)); } private <K> void doReadOnlyEvalManyEmpty(Supplier<K> keySupplier, ReadOnlyMap<K, String> map) { K key1 = keySupplier.get(), key2 = keySupplier.get(), key3 = keySupplier.get(); Traversable<ReadEntryView<K, String>> t = map .evalMany(new HashSet<>(Arrays.asList(key1, key2, key3)), identity()); t.forEach(ro -> assertFalse(ro.find().isPresent())); } public void testLocalUpdateSubsetAndReturnPrevs() { doUpdateSubsetAndReturnPrevs(supplyIntKey(), ro(fmapL1), wo(fmapL2), rw(fmapL2)); } public void testReplUpdateSubsetAndReturnPrevsOnNonOwner() { doUpdateSubsetAndReturnPrevs(supplyKeyForCache(0, REPL), ro(fmapR1), wo(fmapR2), rw(fmapR2)); } public void testReplUpdateSubsetAndReturnPrevsOnOwner() { doUpdateSubsetAndReturnPrevs(supplyKeyForCache(1, REPL), ro(fmapR1), wo(fmapR2), rw(fmapR2)); } public void testDistUpdateSubsetAndReturnPrevsOnNonOwner() { doUpdateSubsetAndReturnPrevs(supplyKeyForCache(0, DIST), ro(fmapD1), wo(fmapD2), rw(fmapD2)); } public void testDistUpdateSubsetAndReturnPrevsOnOwner() { doUpdateSubsetAndReturnPrevs(supplyKeyForCache(1, DIST), ro(fmapD1), wo(fmapD2), rw(fmapD2)); } private <K> void doUpdateSubsetAndReturnPrevs(Supplier<K> keySupplier, ReadOnlyMap<K, String> map1, WriteOnlyMap<K, String> map2, ReadWriteMap<K, String> map3) { K key1 = keySupplier.get(), key2 = keySupplier.get(), key3 = keySupplier.get(); Map<K, String> data = new HashMap<>(); data.put(key1, "one"); data.put(key2, "two"); data.put(key3, "three"); await(map2.evalMany(data, setValueConsumer())); Traversable<String> currentValues = map1.evalMany(data.keySet(), returnReadOnlyFindOrNull()); List<String> collectedValues = currentValues.collect(ArrayList::new, ArrayList::add, ArrayList::addAll); Collections.sort(collectedValues); List<String> dataValues = new ArrayList<>(data.values()); Collections.sort(dataValues); assertEquals(collectedValues, dataValues); Map<K, String> newData = new HashMap<>(); newData.put(key1, "bat"); newData.put(key2, "bi"); newData.put(key3, "hiru"); Traversable<String> prevTraversable = map3.evalMany(newData, setValueReturnPrevOrNull()); List<String> collectedPrev = prevTraversable.collect(ArrayList::new, ArrayList::add, ArrayList::addAll); Collections.sort(collectedPrev); assertEquals(dataValues, collectedPrev); Traversable<String> updatedValues = map1.evalMany(data.keySet(), returnReadOnlyFindOrNull()); List<String> collectedUpdates = updatedValues.collect(ArrayList::new, ArrayList::add, ArrayList::addAll); Collections.sort(collectedUpdates); List<String> newDataValues = new ArrayList<>(newData.values()); Collections.sort(newDataValues); assertEquals(newDataValues, collectedUpdates); } public void testLocalReadWriteToRemoveAllAndReturnPrevs() { doReadWriteToRemoveAllAndReturnPrevs(supplyIntKey(), wo(fmapL1), rw(fmapL2)); } public void testReplReadWriteToRemoveAllAndReturnPrevsOnNonOwner() { doReadWriteToRemoveAllAndReturnPrevs(supplyKeyForCache(0, REPL), wo(fmapR1), rw(fmapR2)); } public void testReplReadWriteToRemoveAllAndReturnPrevsOnOwner() { doReadWriteToRemoveAllAndReturnPrevs(supplyKeyForCache(1, REPL), wo(fmapR1), rw(fmapR2)); } public void testDistReadWriteToRemoveAllAndReturnPrevsOnNonOwner() { doReadWriteToRemoveAllAndReturnPrevs(supplyKeyForCache(0, DIST), wo(fmapD1), rw(fmapD2)); } public void testDistReadWriteToRemoveAllAndReturnPrevsOnOwner() { doReadWriteToRemoveAllAndReturnPrevs(supplyKeyForCache(1, DIST), wo(fmapD1), rw(fmapD2)); } private <K> void doReadWriteToRemoveAllAndReturnPrevs(Supplier<K> keySupplier, WriteOnlyMap<K, String> map1, ReadWriteMap<K, String> map2) { K key1 = keySupplier.get(), key2 = keySupplier.get(), key3 = keySupplier.get(); Map<K, String> data = new HashMap<>(); data.put(key1, "one"); data.put(key2, "two"); data.put(key3, "three"); await(map1.evalMany(data, setValueConsumer())); Traversable<String> prevTraversable = map2.evalAll(removeReturnPrevOrNull()); Set<String> prevValues = prevTraversable.collect(HashSet::new, HashSet::add, HashSet::addAll); assertEquals(new HashSet<>(data.values()), prevValues); } public void testLocalReturnViewFromReadOnlyEval() { doReturnViewFromReadOnlyEval(supplyIntKey(), ro(fmapL1), wo(fmapL2)); } public void testReplReturnViewFromReadOnlyEvalOnNonOwner() { doReturnViewFromReadOnlyEval(supplyKeyForCache(0, REPL), ro(fmapR1), wo(fmapR2)); } public void testReplReturnViewFromReadOnlyEvalOnOwner() { doReturnViewFromReadOnlyEval(supplyKeyForCache(1, REPL), ro(fmapR1), wo(fmapR2)); } public void testDistReturnViewFromReadOnlyEvalOnNonOwner() { doReturnViewFromReadOnlyEval(supplyKeyForCache(0, DIST), ro(fmapD1), wo(fmapD2)); } public void testDistReturnViewFromReadOnlyEvalOnOwner() { doReturnViewFromReadOnlyEval(supplyKeyForCache(1, DIST), ro(fmapD1), wo(fmapD2)); } private <K> void doReturnViewFromReadOnlyEval(Supplier<K> keySupplier, ReadOnlyMap<K, String> ro, WriteOnlyMap<K, String> wo) { K k = keySupplier.get(); assertReadOnlyViewEmpty(k, await(ro.eval(k, identity()))); await(wo.eval(k, setOneWriteOnly())); assertReadOnlyViewEquals(k, "one", await(ro.eval(k, identity()))); } private Consumer<WriteEntryView<String>> setOneWriteOnly() { return (Consumer<WriteEntryView<String>> & Serializable) wv -> wv.set("one"); } public void testLocalReturnViewFromReadWriteEval() { doReturnViewFromReadWriteEval(supplyIntKey(), rw(fmapL1), rw(fmapL2)); } public void testReplReturnViewFromReadWriteEvalOnNonOwner() { doReturnViewFromReadWriteEval(supplyKeyForCache(0, REPL), rw(fmapR1), rw(fmapR2)); } public void testReplReturnViewFromReadWriteEvalOnOwner() { doReturnViewFromReadWriteEval(supplyKeyForCache(1, REPL), rw(fmapR1), rw(fmapR2)); } public void testDistReturnViewFromReadWriteEvalOnNonOwner() { doReturnViewFromReadWriteEval(supplyKeyForCache(0, DIST), rw(fmapD1), rw(fmapD2)); } public void testDistReturnViewFromReadWriteEvalOnOwner() { doReturnViewFromReadWriteEval(supplyKeyForCache(1, DIST), rw(fmapD1), rw(fmapD2)); } private <K> void doReturnViewFromReadWriteEval(Supplier<K> keySupplier, ReadWriteMap<K, String> readMap, ReadWriteMap<K, String> writeMap) { K k = keySupplier.get(); assertReadWriteViewEmpty(k, await(readMap.eval(k, returnReadWriteView()))); assertReadWriteViewEquals(k, "one", await(writeMap.eval(k, setOneReadWrite()))); assertReadWriteViewEquals(k, "one", await(readMap.eval(k, returnReadWriteView()))); assertReadWriteViewEquals(k, "uno", await(writeMap.eval(k, "uno", setValueReturnView()))); assertReadWriteViewEquals(k, "uno", await(readMap.eval(k, returnReadWriteView()))); } private <K> Function<ReadWriteEntryView<K, String>, ReadWriteEntryView<K, String>> setOneReadWrite() { return (Function<ReadWriteEntryView<K, String>, ReadWriteEntryView<K, String>> & Serializable) rw -> { rw.set("one"); return rw; }; } }