blob: 8076ba5a4526de783a7f4be2a7e3098e4e2af0dd [file] [log] [blame]
/*
* Copyright 2015 Open Networking Laboratory
*
* 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 org.onosproject.store.consistent.impl;
import static java.util.Collections.unmodifiableCollection;
import static java.util.Collections.unmodifiableSet;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.*;
import java.util.Collection;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.onlab.util.Match;
import org.onosproject.core.ApplicationId;
import org.onosproject.core.DefaultApplicationId;
import org.onosproject.store.service.Serializer;
import org.onosproject.store.service.Transaction;
import org.onosproject.store.service.Versioned;
import com.google.common.base.MoreObjects;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;
import net.kuujo.copycat.Task;
import net.kuujo.copycat.cluster.Cluster;
import net.kuujo.copycat.resource.ResourceState;
/**
*
*/
public class DefaultAsyncConsistentMapTest {
private static final ApplicationId APP_ID = new DefaultApplicationId(42, "what");
private static final TestData KEY1A = new TestData("One", "a");
private static final TestData KEY1B = new TestData("One", "b");
private static final TestData VALUE2A = new TestData("Two", "a");
private static final TestData VALUE2B = new TestData("Two", "b");
@Before
public void setUp() throws Exception {
}
@After
public void tearDown() throws Exception {
}
@Test
public void testKeySet() throws Exception {
DefaultAsyncConsistentMap<TestData, TestData> map;
String name = "map_name";
Database database = new TestDatabase();
Serializer serializer = Serializer.forTypes(TestData.class);
map = new DefaultAsyncConsistentMap<>(name, APP_ID, database, serializer,
false, false, false);
map.put(KEY1A, VALUE2A);
map.put(KEY1B, VALUE2A);
Set<TestData> set = map.keySet().get();
assertEquals("Should contain 2 keys",
2, set.size());
assertThat(set.contains(KEY1A), is(true));
assertThat(set.contains(KEY1B), is(true));
assertThat(set.contains(new TestData("One", "a")), is(true));
}
@Test
public void testEntrySet() throws Exception {
DefaultAsyncConsistentMap<TestData, TestData> map;
String name = "map_name";
Database database = new TestDatabase();
Serializer serializer = Serializer.forTypes(TestData.class);
map = new DefaultAsyncConsistentMap<>(name, APP_ID, database, serializer,
false, false, false);
map.put(KEY1A, VALUE2A);
map.put(KEY1B, VALUE2A);
assertEquals("Should contain 2 entry",
2,
map.entrySet().get().size());
}
/**
* Object to be used as a test data.
*
* {@link Object#equals(Object)} use only part of it's fields.
*
* As a result there can be 2 instances which the
* serialized bytes are not-equal but
* {@link Object#equals(Object)}-wise they are equal.
*/
public static class TestData {
private final String theKey;
@SuppressWarnings("unused")
private final String notUsedForEquals;
public TestData(String theKey, String notUsedForEquals) {
this.theKey = theKey;
this.notUsedForEquals = notUsedForEquals;
}
@Override
public int hashCode() {
return Objects.hashCode(theKey);
}
@Override
public boolean equals(Object obj) {
if (obj instanceof TestData) {
TestData that = (TestData) obj;
return Objects.equals(this.theKey, that.theKey);
}
return false;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("theKey", theKey)
.add("notUsedForEquals", notUsedForEquals)
.toString();
}
}
/**
* {@link Database} implementation for testing.
*
* There is only 1 backing Map, {@code mapName} will be ignored.
*/
public class TestDatabase implements Database {
Map<String, Versioned<byte[]>> map = new ConcurrentHashMap<>();
@Override
public CompletableFuture<Set<String>> maps() {
return CompletableFuture.completedFuture(ImmutableSet.of());
}
@Override
public CompletableFuture<Map<String, Long>> counters() {
return CompletableFuture.completedFuture(ImmutableMap.of());
}
@Override
public CompletableFuture<Integer> mapSize(String mapName) {
return CompletableFuture.completedFuture(map.size());
}
@Override
public CompletableFuture<Boolean> mapIsEmpty(String mapName) {
return CompletableFuture.completedFuture(map.isEmpty());
}
@Override
public CompletableFuture<Boolean> mapContainsKey(String mapName,
String key) {
return CompletableFuture.completedFuture(map.containsKey(key));
}
@Override
public CompletableFuture<Boolean> mapContainsValue(String mapName,
byte[] value) {
return CompletableFuture.completedFuture(Maps.transformValues(map, Versioned::value)
.containsValue(value));
}
@Override
public CompletableFuture<Versioned<byte[]>> mapGet(String mapName,
String key) {
return CompletableFuture.completedFuture(map.get(key));
}
@Override
public synchronized CompletableFuture<Result<UpdateResult<String, byte[]>>> mapUpdate(String mapName,
String key,
Match<byte[]> valueMatch,
Match<Long> versionMatch,
byte[] value) {
boolean updated = false;
final Versioned<byte[]> oldValue;
final Versioned<byte[]> newValue;
Versioned<byte[]> old = map.getOrDefault(key, new Versioned<byte[]>(null, 0));
if (valueMatch.matches(old.value()) && versionMatch.matches(old.version())) {
updated = true;
oldValue = old;
newValue = new Versioned<>(value, old.version() + 1);
map.put(key, newValue);
} else {
updated = false;
oldValue = old;
newValue = old;
}
return CompletableFuture.completedFuture(
Result.ok(new UpdateResult<String, byte[]>(updated,
mapName, key, oldValue, newValue)));
}
@Override
public CompletableFuture<Result<Void>> mapClear(String mapName) {
throw new UnsupportedOperationException();
}
@Override
public CompletableFuture<Set<String>> mapKeySet(String mapName) {
return CompletableFuture.completedFuture(unmodifiableSet(map.keySet()));
}
@Override
public CompletableFuture<Collection<Versioned<byte[]>>> mapValues(String mapName) {
return CompletableFuture.completedFuture(unmodifiableCollection(map.values()));
}
@Override
public CompletableFuture<Set<Entry<String, Versioned<byte[]>>>> mapEntrySet(String mapName) {
return CompletableFuture.completedFuture(unmodifiableSet(map.entrySet()));
}
@Override
public CompletableFuture<Long> counterAddAndGet(String counterName,
long delta) {
throw new UnsupportedOperationException();
}
@Override
public CompletableFuture<Long> counterGetAndAdd(String counterName,
long delta) {
throw new UnsupportedOperationException();
}
@Override
public CompletableFuture<Void> counterSet(String counterName,
long value) {
throw new UnsupportedOperationException();
}
@Override
public CompletableFuture<Boolean> counterCompareAndSet(String counterName,
long expectedValue,
long update) {
throw new UnsupportedOperationException();
}
@Override
public CompletableFuture<Long> counterGet(String counterName) {
throw new UnsupportedOperationException();
}
@Override
public CompletableFuture<Long> queueSize(String queueName) {
throw new UnsupportedOperationException();
}
@Override
public CompletableFuture<Void> queuePush(String queueName,
byte[] entry) {
throw new UnsupportedOperationException();
}
@Override
public CompletableFuture<byte[]> queuePop(String queueName) {
throw new UnsupportedOperationException();
}
@Override
public CompletableFuture<byte[]> queuePeek(String queueName) {
throw new UnsupportedOperationException();
}
@Override
public CompletableFuture<CommitResponse> prepareAndCommit(Transaction transaction) {
throw new UnsupportedOperationException();
}
@Override
public CompletableFuture<Boolean> prepare(Transaction transaction) {
throw new UnsupportedOperationException();
}
@Override
public CompletableFuture<CommitResponse> commit(Transaction transaction) {
throw new UnsupportedOperationException();
}
@Override
public CompletableFuture<Boolean> rollback(Transaction transaction) {
throw new UnsupportedOperationException();
}
@Override
public String name() {
return "name";
}
@Override
public ResourceState state() {
return ResourceState.HEALTHY;
}
@Override
public Cluster cluster() {
throw new UnsupportedOperationException();
}
@Override
public Database addStartupTask(Task<CompletableFuture<Void>> task) {
throw new UnsupportedOperationException();
}
@Override
public Database addShutdownTask(Task<CompletableFuture<Void>> task) {
throw new UnsupportedOperationException();
}
@Override
public CompletableFuture<Database> open() {
return CompletableFuture.completedFuture(this);
}
@Override
public boolean isOpen() {
return true;
}
@Override
public CompletableFuture<Void> close() {
return CompletableFuture.completedFuture(null);
}
@Override
public boolean isClosed() {
return false;
}
@Override
public void registerConsumer(Consumer<StateMachineUpdate> consumer) {
}
@Override
public void unregisterConsumer(Consumer<StateMachineUpdate> consumer) {
}
}
}