Package voldemort

Source Code of voldemort.TestUtils

/*
* Copyright 2008-2013 LinkedIn, Inc
*
* 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 voldemort;

import java.io.File;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collections;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Random;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;

import junit.framework.AssertionFailedError;
import voldemort.cluster.Cluster;
import voldemort.cluster.Node;
import voldemort.routing.RoutingStrategy;
import voldemort.routing.RoutingStrategyFactory;
import voldemort.routing.StoreRoutingPlan;
import voldemort.store.Store;
import voldemort.store.StoreDefinition;
import voldemort.utils.ByteArray;
import voldemort.utils.Utils;
import voldemort.versioning.ClockEntry;
import voldemort.versioning.VectorClock;
import voldemort.versioning.Versioned;
import voldemort.xml.StoreDefinitionsMapper;

import com.google.common.collect.Lists;
import com.google.common.io.Files;

/**
* Helper utilities for tests
*
*
*/
public class TestUtils {

    public static final String DIGITS = "0123456789";
    public static final String LETTERS = "qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM";
    public static final String CHARACTERS = LETTERS + DIGITS + "~!@#$%^&*()____+-=[];',,,./>?:{}";
    public static final Long SEED = System.currentTimeMillis();
    public static final Random SEEDED_RANDOM = new Random(SEED);
    public static final Random UNSEEDED_RANDOM = new Random();
    private static final CharSequence VOLD_TEST_DATA_DIRECTORY_NAME = "vold-test-data";

    /**
     * Get a vector clock with events on the sequence of nodes given So
     * getClock(1,1,2,2,2) means a clock that has two writes on node 1 and 3
     * writes on node 2.
     *
     * @param nodes The sequence of nodes
     * @return A VectorClock initialized with the given sequence of events
     */
    public static VectorClock getClock(int... nodes) {
        VectorClock clock = new VectorClock();
        increment(clock, nodes);
        return clock;
    }

    public static VectorClock getClockWithTs(long ts, int... nodes) {
        VectorClock clock = new VectorClock(ts);
        increment(clock, nodes);
        return clock;
    }

    /**
     * Constructs a vector clock as in versioned put based on timestamp
     *
     * @param timeMs timestamp to use in the clock value
     * @param master node for the put. the master's versions will be timeMs+1,
     *        if master > -1
     * @return
     */
    @SuppressWarnings("deprecation")
    public static VectorClock getVersionedPutClock(long timeMs, int master, int... nodes) {
        List<ClockEntry> clockEntries = Lists.newArrayList();
        for(int node: nodes) {
            if(master >= 0 && node == master) {
                clockEntries.add(new ClockEntry((short) node, timeMs + 1));
            } else {
                clockEntries.add(new ClockEntry((short) node, timeMs));
            }
        }
        return new VectorClock(clockEntries, timeMs);
    }

    /**
     * Helper method to construct Versioned byte value.
     *
     * @param nodes See getClock method for explanation of this argument
     * @return
     */
    public static Versioned<byte[]> getVersioned(byte[] value, int... nodes) {
        return new Versioned<byte[]>(value, getClock(nodes));
    }

    /**
     * Returns true if both the versioned lists are equal, in terms of values
     * and vector clocks, taking into consideration, they might be in different
     * orders as well. Ignores the timestamps as a part of the vector clock
     *
     * @param first
     * @param second
     * @return
     */
    public static boolean areVersionedListsEqual(List<Versioned<byte[]>> first,
                                                 List<Versioned<byte[]>> second) {
        if(first.size() != second.size())
            return false;
        // find a match for every first element in second list
        for(Versioned<byte[]> firstElement: first) {
            boolean found = false;
            for(Versioned<byte[]> secondElement: second) {
                if(firstElement.equals(secondElement)) {
                    found = true;
                    break;
                }
            }
            if(!found)
                return false;
        }

        // find a match for every second element in first list
        for(Versioned<byte[]> secondElement: second) {
            boolean found = false;
            for(Versioned<byte[]> firstElement: first) {
                if(firstElement.equals(secondElement)) {
                    found = true;
                    break;
                }
            }
            if(!found)
                return false;
        }
        return true;
    }

    /**
     * Record events for the given sequence of nodes
     *
     * @param clock The VectorClock to record the events on
     * @param nodes The sequences of node events
     */
    public static void increment(VectorClock clock, int... nodes) {
        for(int n: nodes)
            clock.incrementVersion((short) n, clock.getTimestamp());
    }

    /**
     * Test two byte arrays for (deep) equality. I think this exists in java 6
     * but not java 5
     *
     * @param a1 Array 1
     * @param a2 Array 2
     * @return True iff a1.length == a2.length and a1[i] == a2[i] for 0 <= i <
     *         a1.length
     */
    public static boolean bytesEqual(byte[] a1, byte[] a2) {
        if(a1 == a2) {
            return true;
        } else if(a1 == null || a2 == null) {
            return false;
        } else if(a1.length != a2.length) {
            return false;
        } else {
            for(int i = 0; i < a1.length; i++)
                if(a1[i] != a2[i])
                    return false;
        }

        return true;
    }

    /**
     * Create a string with some random letters
     *
     * @param length The length of the string to create
     * @return The string
     */
    public static String randomLetters(int length) {
        return randomString(LETTERS, length);
    }

    /**
     * Create a string that is a random sample (with replacement) from the given
     * string
     *
     * @param sampler The string to sample from
     * @param length The length of the string to create
     * @return The created string
     */
    public static String randomString(String sampler, int length) {
        StringBuilder builder = new StringBuilder(length);
        for(int i = 0; i < length; i++)
            builder.append(sampler.charAt(SEEDED_RANDOM.nextInt(sampler.length())));
        return builder.toString();
    }

    /**
     * Generate an array of random bytes
     *
     * @param length
     * @return
     */
    public static byte[] randomBytes(int length) {
        byte[] bytes = new byte[length];
        SEEDED_RANDOM.nextBytes(bytes);
        return bytes;
    }

    public static <K, V, T> void assertContains(Store<K, V, T> store, K key, V... values) {
        List<Versioned<V>> found = store.get(key, null);
        if(found.size() != values.length)
            throw new AssertionFailedError("Expected to find " + values.length
                                           + " values in store, but found only " + found.size()
                                           + ".");
        for(V v: values) {
            boolean isFound = false;
            for(Versioned<V> f: found)
                if(Utils.deepEquals(f.getValue(), v))
                    isFound = true;
            if(!isFound)
                throw new AssertionFailedError("Could not find value " + v + " in results.");
        }
    }

    /**
     * Return an array of length count containing random integers in the range
     * (0, max) generated off the test rng.
     *
     * @param max The bound on the random number size
     * @param count The number of integers to generate
     * @return The array of integers
     */
    public static int[] randomInts(int max, int count) {
        int[] vals = new int[count];
        for(int i = 0; i < count; i++)
            vals[i] = SEEDED_RANDOM.nextInt(max);
        return vals;
    }

    /**
     * Weirdly java doesn't seem to have Arrays.shuffle(), this terrible hack
     * does that.
     *
     * @return A shuffled copy of the input
     */
    public static int[] shuffle(int[] input) {
        List<Integer> vals = new ArrayList<Integer>(input.length);
        for(int i = 0; i < input.length; i++)
            vals.add(input[i]);
        Collections.shuffle(vals, SEEDED_RANDOM);
        int[] copy = new int[input.length];
        for(int i = 0; i < input.length; i++)
            copy[i] = vals.get(i);
        return copy;
    }

    /**
     * Compute the requested quantile of the given array
     *
     * @param values The array of values
     * @param quantile The quantile requested (must be between 0.0 and 1.0
     *        inclusive)
     * @return The quantile
     */
    public static long quantile(long[] values, double quantile) {
        if(values == null)
            throw new IllegalArgumentException("Values cannot be null.");
        if(quantile < 0.0 || quantile > 1.0)
            throw new IllegalArgumentException("Quantile must be between 0.0 and 1.0");

        long[] copy = new long[values.length];
        System.arraycopy(values, 0, copy, 0, copy.length);
        Arrays.sort(copy);
        int index = (int) (copy.length * quantile);
        return copy[index];
    }

    /**
     * Compute the mean of the given values
     *
     * @param values The values
     * @return The mean
     */
    public static double mean(long[] values) {
        double total = 0.0;
        for(int i = 0; i < values.length; i++)
            total += values[i];
        return total / values.length;
    }

    /**
     * Create a temporary directory in the directory given by java.io.tmpdir
     *
     * @return The directory created.
     */
    public static File createTempDir() {
        String systemTempDir = System.getProperty("java.io.tmpdir");
        if(!(systemTempDir.contains(VOLD_TEST_DATA_DIRECTORY_NAME))) {
            File parentDir = new File(systemTempDir + "/" + VOLD_TEST_DATA_DIRECTORY_NAME);
            parentDir.delete();
            parentDir.mkdir();
            parentDir.deleteOnExit();
            System.setProperty("java.io.tmpdir", parentDir.getAbsolutePath());
        }
        File tempdir = Files.createTempDir();
        tempdir.delete();
        tempdir.mkdir();
        tempdir.deleteOnExit();
        return tempdir;
    }

    /**
     * Create a temporary directory that is a child of the given directory
     *
     * @param parent The parent directory
     * @return The temporary directory
     */
    public static File createTempDir(File parent) {
        File temp = new File(parent,
                             Integer.toString(Math.abs(UNSEEDED_RANDOM.nextInt()) % 1000000));
        temp.delete();
        temp.mkdir();
        temp.deleteOnExit();
        return temp;
    }

    /**
     * Wrap the given string in quotation marks. This is slightly more readable
     * then the java inline quotes that require escaping.
     *
     * @param s The string to wrap in quotes
     * @return The string
     */
    public static String quote(String s) {
        return "\"" + s + "\"";
    }

    public static List<Node> createNodes(int[][] partitionMap) {
        ArrayList<Node> nodes = new ArrayList<Node>(partitionMap.length);
        ArrayList<Integer> partitionList = new ArrayList<Integer>();

        for(int i = 0; i < partitionMap.length; i++) {
            partitionList.clear();
            for(int p = 0; p < partitionMap[i].length; p++) {
                partitionList.add(partitionMap[i][p]);
            }
            nodes.add(new Node(i, "localhost", 8880 + i, 6666 + i, 7000 + i, partitionList));
        }

        return nodes;
    }

    public static int getMissingPartitionsSize(Cluster orig, Cluster updated) {
        int diffPartition = 0;
        ArrayList<Node> nodeAList = new ArrayList<Node>(orig.getNodes());

        for(int i = 0; i < orig.getNodes().size(); i++) {
            Node nodeA = nodeAList.get(i);
            Node nodeB;
            try {
                nodeB = updated.getNodeById(nodeA.getId());
            } catch(VoldemortException e) {
                // add the partition in this node
                diffPartition += nodeA.getNumberOfPartitions();
                continue;
            }

            SortedSet<Integer> bPartitonSet = new TreeSet<Integer>(nodeB.getPartitionIds());
            for(int p: nodeA.getPartitionIds()) {
                if(!bPartitonSet.contains(p)) {
                    diffPartition++;
                }
            }
        }
        return diffPartition;
    }

    /**
     * Given a StoreRoutingPlan generates upto numKeysPerPartition keys per
     * partition
     *
     * @param numKeysPerPartition
     * @return a hashmap of partition to list of keys generated
     */
    public static HashMap<Integer, List<byte[]>> createPartitionsKeys(StoreRoutingPlan routingPlan,
                                                                      int numKeysPerPartition) {
        HashMap<Integer, List<byte[]>> partitionToKeyList = new HashMap<Integer, List<byte[]>>();
        Set<Integer> partitionsPending = new HashSet<Integer>(routingPlan.getCluster()
                                                                         .getNumberOfPartitions());
        for(int partition = 0; partition < routingPlan.getCluster().getNumberOfPartitions(); partition++) {
            partitionsPending.add(partition);
            partitionToKeyList.put(partition, new ArrayList<byte[]>(numKeysPerPartition));
        }

        for(int key = 0;; key++) {
            byte[] keyBytes = ("key" + key).getBytes();
            int partition = routingPlan.getMasterPartitionId(keyBytes);
            if(partitionToKeyList.get(partition).size() < numKeysPerPartition) {
                partitionToKeyList.get(partition).add(keyBytes);
                if(partitionToKeyList.get(partition).size() == numKeysPerPartition) {
                    partitionsPending.remove(partition);
                }
            }
            if(partitionsPending.size() == 0) {
                break;
            }
        }
        return partitionToKeyList;
    }

    /**
     * Always uses UTF-8.
     */
    public static ByteArray toByteArray(String s) {
        try {
            return new ByteArray(s.getBytes("UTF-8"));
        } catch(UnsupportedEncodingException e) {
            /* Should not happen */
            throw new IllegalStateException(e);
        }
    }

    public static void assertWithBackoff(long timeout, Attempt attempt) throws Exception {
        assertWithBackoff(30, timeout, attempt);
    }

    public static void assertWithBackoff(long initialDelay, long timeout, Attempt attempt)
            throws Exception {
        long delay = initialDelay;
        long finishBy = System.currentTimeMillis() + timeout;

        while(true) {
            try {
                attempt.checkCondition();
                return;
            } catch(AssertionError e) {
                if(System.currentTimeMillis() < finishBy) {
                    Thread.sleep(delay);
                    delay *= 2;
                } else {
                    throw e;
                }
            }
        }
    }

    /**
     * Because java.beans.ReflectionUtils isn't public...
     */

    @SuppressWarnings("unchecked")
    public static <T> T getPrivateValue(Object instance, String fieldName) throws Exception {
        Field eventDataQueueField = instance.getClass().getDeclaredField(fieldName);
        eventDataQueueField.setAccessible(true);
        return (T) eventDataQueueField.get(instance);
    }

    /**
     * Wrapper to get a StoreDefinition object constructed, given a store name
     */
    public static StoreDefinition makeStoreDefinition(String storeName) {
        return new StoreDefinition(storeName,
                                   null,
                                   null,
                                   null,
                                   null,
                                   null,
                                   null,
                                   null,
                                   0,
                                   null,
                                   0,
                                   null,
                                   0,
                                   null,
                                   null,
                                   null,
                                   null,
                                   null,
                                   null,
                                   null,
                                   null,
                                   null,
                                   null,
                                   null,
                                   null,
                                   0);
    }

    /**
     * Wrapper to get a StoreDefinition object constructed, given a store name,
     * memory foot print
     */
    public static StoreDefinition makeStoreDefinition(String storeName, long memFootprintMB) {
        return new StoreDefinition(storeName,
                                   null,
                                   null,
                                   null,
                                   null,
                                   null,
                                   null,
                                   null,
                                   0,
                                   null,
                                   0,
                                   null,
                                   0,
                                   null,
                                   null,
                                   null,
                                   null,
                                   null,
                                   null,
                                   null,
                                   null,
                                   null,
                                   null,
                                   null,
                                   null,
                                   memFootprintMB);
    }

    /**
     * Provides a routing strategy for local tests to work with
     *
     * @return
     */
    public static RoutingStrategy makeSingleNodeRoutingStrategy() {
        Cluster cluster = VoldemortTestConstants.getOneNodeCluster();
        StoreDefinitionsMapper mapper = new StoreDefinitionsMapper();
        List<StoreDefinition> storeDefs = mapper.readStoreList(new StringReader(VoldemortTestConstants.getSingleStoreDefinitionsXml()));
        return new RoutingStrategyFactory().updateRoutingStrategy(storeDefs.get(0), cluster);
    }

    /**
     * Constructs a calendar object representing the given time
     */
    public static GregorianCalendar getCalendar(int year,
                                                int month,
                                                int day,
                                                int hour,
                                                int mins,
                                                int secs) {
        GregorianCalendar cal = new GregorianCalendar();
        cal.set(Calendar.YEAR, year);
        cal.set(Calendar.MONTH, month);
        cal.set(Calendar.DATE, day);
        cal.set(Calendar.HOUR_OF_DAY, hour);
        cal.set(Calendar.MINUTE, mins);
        cal.set(Calendar.SECOND, secs);
        cal.set(Calendar.MILLISECOND, 0);
        return cal;
    }
}
TOP

Related Classes of voldemort.TestUtils

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.