org.onosproject.store.primitives.resources.impl.AsyncConsistentSetMultimapTest.java Source code

Java tutorial

Introduction

Here is the source code for org.onosproject.store.primitives.resources.impl.AsyncConsistentSetMultimapTest.java

Source

/*
 * Copyright 2016 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.primitives.resources.impl;

import com.google.common.collect.Lists;
import com.google.common.collect.Multiset;
import com.google.common.collect.TreeMultiset;
import com.google.common.io.Files;
import io.atomix.catalyst.transport.Address;
import io.atomix.catalyst.transport.LocalTransport;
import io.atomix.copycat.server.CopycatServer;
import io.atomix.copycat.server.storage.Storage;
import io.atomix.copycat.server.storage.StorageLevel;
import io.atomix.manager.state.ResourceManagerState;
import io.atomix.resource.ResourceType;
import org.apache.commons.collections.keyvalue.DefaultMapEntry;
import org.junit.Ignore;
import org.junit.Test;
import org.onlab.util.Tools;

import java.io.File;
import java.time.Duration;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import java.util.Map;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

/**
 * Tests the {@link AsyncConsistentSetMultimap}.
 */
public class AsyncConsistentSetMultimapTest extends AtomixTestBase {
    private final File testDir = Files.createTempDir();
    private final String keyOne = "hello";
    private final String keyTwo = "goodbye";
    private final String keyThree = "foo";
    private final String keyFour = "bar";
    private final byte[] valueOne = Tools.getBytesUtf8(keyOne);
    private final byte[] valueTwo = Tools.getBytesUtf8(keyTwo);
    private final byte[] valueThree = Tools.getBytesUtf8(keyThree);
    private final byte[] valueFour = Tools.getBytesUtf8(keyFour);
    private final List<String> allKeys = Lists.newArrayList(keyOne, keyTwo, keyThree, keyFour);
    private final List<byte[]> allValues = Lists.newArrayList(valueOne, valueTwo, valueThree, valueFour);

    @Override
    protected ResourceType resourceType() {
        return new ResourceType(AsyncConsistentSetMultimap.class);
    }

    /**
     * Test that size behaves correctly (This includes testing of the empty
     * check).
     */
    @Ignore
    @Test
    public void testSize() throws Throwable {
        clearTests();
        AsyncConsistentSetMultimap map = createResource(3);
        //Simplest operation case
        map.isEmpty().thenAccept(result -> assertTrue(result));
        map.put(keyOne, valueOne).thenAccept(result -> assertTrue(result)).join();
        map.isEmpty().thenAccept(result -> assertFalse(result));
        map.size().thenAccept(result -> assertEquals(1, (int) result)).join();
        //Make sure sizing is dependent on values not keys
        map.put(keyOne, valueTwo).thenAccept(result -> assertTrue(result)).join();
        map.size().thenAccept(result -> assertEquals(2, (int) result)).join();
        //Ensure that double adding has no effect
        map.put(keyOne, valueOne).thenAccept(result -> assertFalse(result)).join();
        map.size().thenAccept(result -> assertEquals(2, (int) result)).join();
        //Check handling for multiple keys
        map.put(keyTwo, valueOne).thenAccept(result -> assertTrue(result)).join();
        map.put(keyTwo, valueTwo).thenAccept(result -> assertTrue(result)).join();
        map.size().thenAccept(result -> assertEquals(4, (int) result)).join();
        //Check size with removal
        map.remove(keyOne, valueOne).thenAccept(result -> assertTrue(result)).join();
        map.size().thenAccept(result -> assertEquals(3, (int) result)).join();
        //Check behavior under remove of non-existant key
        map.remove(keyOne, valueOne).thenAccept(result -> assertFalse(result)).join();
        map.size().thenAccept(result -> assertEquals(3, (int) result)).join();
        //Check clearing the entirety of the map
        map.clear().join();
        map.size().thenAccept(result -> assertEquals(0, (int) result)).join();
        map.isEmpty().thenAccept(result -> assertTrue(result));

        map.destroy().join();
        clearTests();
    }

    /**
     * Contains tests for value, key and entry.
     */
    @Ignore
    @Test
    public void containsTest() throws Throwable {
        clearTests();
        AsyncConsistentSetMultimap map = createResource(3);

        //Populate the maps
        allKeys.forEach(key -> {
            map.putAll(key, allValues).thenAccept(result -> assertTrue(result)).join();
        });
        map.size().thenAccept(result -> assertEquals(16, (int) result)).join();

        //Test key contains positive results
        allKeys.forEach(key -> {
            map.containsKey(key).thenAccept(result -> assertTrue(result)).join();
        });

        //Test value contains positive results
        allValues.forEach(value -> {
            map.containsValue(value).thenAccept(result -> assertTrue(result)).join();
        });

        //Test contains entry for all possible entries
        allKeys.forEach(key -> {
            allValues.forEach(value -> {
                map.containsEntry(key, value).thenAccept(result -> assertTrue(result)).join();
            });
        });

        //Test behavior after removals
        allValues.forEach(value -> {
            final String[] removedKey = new String[1];
            allKeys.forEach(key -> {
                map.remove(key, value).thenAccept(result -> assertTrue(result)).join();
                map.containsEntry(key, value).thenAccept(result -> assertFalse(result)).join();
                removedKey[0] = key;
            });
            //Check that contains key works properly for removed keys
            map.containsKey(removedKey[0]).thenAccept(result -> assertFalse(result));
        });

        //Check that contains value works correctly for removed values
        allValues.forEach(value -> {
            map.containsValue(value).thenAccept(result -> assertFalse(result)).join();
        });

        map.destroy().join();
        clearTests();
    }

    /**
     * Contains tests for put, putAll, remove, removeAll and replace.
     * @throws Exception
     */
    @Ignore
    @Test
    public void addAndRemoveTest() throws Exception {
        clearTests();
        AsyncConsistentSetMultimap map = createResource(3);

        //Test single put
        allKeys.forEach(key -> {
            //Value should actually be added here
            allValues.forEach(value -> {
                map.put(key, value).thenAccept(result -> assertTrue(result)).join();
                //Duplicate values should be ignored here
                map.put(key, value).thenAccept(result -> assertFalse(result)).join();
            });
        });

        //Test single remove
        allKeys.forEach(key -> {
            //Value should actually be added here
            allValues.forEach(value -> {
                map.remove(key, value).thenAccept(result -> assertTrue(result)).join();
                //Duplicate values should be ignored here
                map.remove(key, value).thenAccept(result -> assertFalse(result)).join();
            });
        });

        map.isEmpty().thenAccept(result -> assertTrue(result)).join();

        //Test multi put
        allKeys.forEach(key -> {
            map.putAll(key, Lists.newArrayList(allValues.subList(0, 2))).thenAccept(result -> assertTrue(result))
                    .join();
            map.putAll(key, Lists.newArrayList(allValues.subList(0, 2))).thenAccept(result -> assertFalse(result))
                    .join();
            map.putAll(key, Lists.newArrayList(allValues.subList(2, 4))).thenAccept(result -> assertTrue(result))
                    .join();
            map.putAll(key, Lists.newArrayList(allValues.subList(2, 4))).thenAccept(result -> assertFalse(result))
                    .join();

        });

        //Test multi remove
        allKeys.forEach(key -> {
            //Split the lists to test how multiRemove can work piecewise
            map.removeAll(key, Lists.newArrayList(allValues.subList(0, 2))).thenAccept(result -> assertTrue(result))
                    .join();
            map.removeAll(key, Lists.newArrayList(allValues.subList(0, 2)))
                    .thenAccept(result -> assertFalse(result)).join();
            map.removeAll(key, Lists.newArrayList(allValues.subList(2, 4))).thenAccept(result -> assertTrue(result))
                    .join();
            map.removeAll(key, Lists.newArrayList(allValues.subList(2, 4)))
                    .thenAccept(result -> assertFalse(result)).join();
        });

        map.isEmpty().thenAccept(result -> assertTrue(result)).join();

        //Repopulate for next test
        allKeys.forEach(key -> {
            map.putAll(key, allValues).thenAccept(result -> assertTrue(result)).join();
        });

        map.size().thenAccept(result -> assertEquals(16, (int) result)).join();

        //Test removeAll of entire entry
        allKeys.forEach(key -> {
            map.removeAll(key).thenAccept(result -> {
                assertTrue(byteArrayCollectionIsEqual(allValues, result.value()));
            }).join();
            map.removeAll(key).thenAccept(result -> {
                assertFalse(byteArrayCollectionIsEqual(allValues, result.value()));
            }).join();
        });

        map.isEmpty().thenAccept(result -> assertTrue(result)).join();

        //Repopulate for next test
        allKeys.forEach(key -> {
            map.putAll(key, allValues).thenAccept(result -> assertTrue(result)).join();
        });

        map.size().thenAccept(result -> assertEquals(16, (int) result)).join();

        allKeys.forEach(key -> {
            map.replaceValues(key, allValues)
                    .thenAccept(result -> assertTrue(byteArrayCollectionIsEqual(allValues, result.value()))).join();
            map.replaceValues(key, Lists.newArrayList())
                    .thenAccept(result -> assertTrue(byteArrayCollectionIsEqual(allValues, result.value()))).join();
            map.replaceValues(key, allValues).thenAccept(result -> assertTrue(result.value().isEmpty())).join();
        });

        //Test replacements of partial sets
        map.size().thenAccept(result -> assertEquals(16, (int) result)).join();

        allKeys.forEach(key -> {
            map.remove(key, valueOne).thenAccept(result -> assertTrue(result)).join();
            map.replaceValues(key, Lists.newArrayList()).thenAccept(result -> assertTrue(byteArrayCollectionIsEqual(
                    Lists.newArrayList(valueTwo, valueThree, valueFour), result.value()))).join();
            map.replaceValues(key, allValues).thenAccept(result -> assertTrue(result.value().isEmpty())).join();
        });

        map.destroy().join();
        clearTests();
    }

    /**
     * Tests the get, keySet, keys, values, and entries implementations as well
     * as a trivial test of the asMap functionality (throws error).
     * @throws Exception
     */
    @Ignore
    @Test
    public void testAccessors() throws Exception {
        clearTests();
        AsyncConsistentSetMultimap map = createResource(3);

        //Populate for full map behavior tests
        allKeys.forEach(key -> {
            map.putAll(key, allValues).thenAccept(result -> assertTrue(result)).join();
        });

        map.size().thenAccept(result -> assertEquals(16, (int) result)).join();

        allKeys.forEach(key -> {
            map.get(key).thenAccept(result -> {
                assertTrue(byteArrayCollectionIsEqual(allValues, result.value()));
            }).join();
        });

        //Test that the key set is correct
        map.keySet().thenAccept(result -> assertTrue(stringArrayCollectionIsEqual(allKeys, result))).join();
        //Test that the correct set and occurrence of values are found in the
        //values result
        map.values().thenAccept(result -> {
            final Multiset<byte[]> set = TreeMultiset.create(new ByteArrayComparator());
            for (int i = 0; i < 4; i++) {
                set.addAll(allValues);
            }
            assertEquals(16, result.size());
            result.forEach(value -> assertTrue(set.remove(value)));
            assertTrue(set.isEmpty());

        }).join();

        //Test that keys returns the right result including the correct number
        //of each item
        map.keys().thenAccept(result -> {
            final Multiset<String> set = TreeMultiset.create();
            for (int i = 0; i < 4; i++) {
                set.addAll(allKeys);
            }
            assertEquals(16, result.size());
            result.forEach(value -> assertTrue(set.remove(value)));
            assertTrue(set.isEmpty());

        }).join();

        //Test that the right combination of key, value pairs are present
        map.entries().thenAccept(result -> {
            final Multiset<Map.Entry<String, byte[]>> set = TreeMultiset.create(new EntryComparator());
            allKeys.forEach(key -> {
                allValues.forEach(value -> {
                    set.add(new DefaultMapEntry(key, value));
                });
            });
            assertEquals(16, result.size());
            result.forEach(entry -> assertTrue(set.remove(entry)));
            assertTrue(set.isEmpty());
        }).join();

        //Testing for empty map behavior
        map.clear().join();

        allKeys.forEach(key -> {
            map.get(key).thenAccept(result -> {
                assertTrue(result.value().isEmpty());
            }).join();
        });

        map.keySet().thenAccept(result -> assertTrue(result.isEmpty())).join();
        map.values().thenAccept(result -> assertTrue(result.isEmpty())).join();
        map.keys().thenAccept(result -> assertTrue(result.isEmpty())).join();
        map.entries().thenAccept(result -> assertTrue(result.isEmpty())).join();

        map.destroy();
        clearTests();
    }

    private AsyncConsistentSetMultimap createResource(int clusterSize) {
        try {
            createCopycatServers(clusterSize);
            AsyncConsistentSetMultimap map = createAtomixClient()
                    .getResource("testMap", AsyncConsistentSetMultimap.class).join();
            return map;
        } catch (Throwable e) {
            throw new RuntimeException(e.toString());
        }
    }

    @Override
    protected CopycatServer createCopycatServer(Address address) {
        CopycatServer server = CopycatServer.builder(address, members).withTransport(new LocalTransport(registry))
                .withStorage(Storage.builder().withStorageLevel(StorageLevel.MEMORY)
                        .withDirectory(testDir + "/" + address.port()).build())
                .withStateMachine(ResourceManagerState::new).withSerializer(serializer.clone())
                .withHeartbeatInterval(Duration.ofMillis(25)).withElectionTimeout(Duration.ofMillis(50))
                .withSessionTimeout(Duration.ofMillis(100)).build();
        copycatServers.add(server);
        return server;
    }

    /**
     * Returns two arrays contain the same set of elements,
     * regardless of order.
     * @param o1 first collection
     * @param o2 second collection
     * @return true if they contain the same elements
     */
    private boolean byteArrayCollectionIsEqual(Collection<? extends byte[]> o1, Collection<? extends byte[]> o2) {
        if (o1 == null || o2 == null || o1.size() != o2.size()) {
            return false;
        }
        for (byte[] array1 : o1) {
            boolean matched = false;
            for (byte[] array2 : o2) {
                if (Arrays.equals(array1, array2)) {
                    matched = true;
                    break;
                }
            }
            if (!matched) {
                return false;
            }
        }
        return true;
    }

    /**
     * Compares two collections of strings returns true if they contain the
     * same strings, false otherwise.
     * @param s1 string collection one
     * @param s2 string collection two
     * @return true if the two sets contain the same strings
     */
    private boolean stringArrayCollectionIsEqual(Collection<? extends String> s1, Collection<? extends String> s2) {
        if (s1 == null || s2 == null || s1.size() != s2.size()) {
            return false;
        }
        for (String string1 : s1) {
            boolean matched = false;
            for (String string2 : s2) {
                if (string1.equals(string2)) {
                    matched = true;
                    break;
                }
            }
            if (!matched) {
                return false;
            }
        }
        return true;
    }

    /**
     * Byte array comparator implementation.
     */
    private class ByteArrayComparator implements Comparator<byte[]> {

        @Override
        public int compare(byte[] o1, byte[] o2) {
            if (Arrays.equals(o1, o2)) {
                return 0;
            } else {
                for (int i = 0; i < o1.length && i < o2.length; i++) {
                    if (o1[i] < o2[i]) {
                        return -1;
                    } else if (o1[i] > o2[i]) {
                        return 1;
                    }
                }
                return o1.length > o2.length ? 1 : -1;
            }
        }
    }

    /**
     * Entry comparator, uses both key and value to determine equality,
     * for comparison falls back to the default string comparator.
     */
    private class EntryComparator implements Comparator<Map.Entry<String, byte[]>> {

        @Override
        public int compare(Map.Entry<String, byte[]> o1, Map.Entry<String, byte[]> o2) {
            if (o1 == null || o1.getKey() == null || o2 == null || o2.getKey() == null) {
                throw new IllegalArgumentException();
            }
            if (o1.getKey().equals(o2.getKey()) && Arrays.equals(o1.getValue(), o2.getValue())) {
                return 0;
            } else {
                return o1.getKey().compareTo(o2.getKey());
            }
        }
    }
}