org.apache.hadoop.util.TestGSet.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.hadoop.util.TestGSet.java

Source

/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.apache.hadoop.util;

import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.Random;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.HadoopIllegalArgumentException;
import org.apache.hadoop.util.Time;
import org.junit.Assert;
import org.junit.Test;

public class TestGSet {
    private static final Random ran = new Random();
    private static final long starttime = Time.now();
    private static final Log LOG = LogFactory.getLog(TestGSet.class);

    private static void print(Object s) {
        System.out.print(s);
        System.out.flush();
    }

    private static void println(Object s) {
        System.out.println(s);
    }

    @Test
    public void testExceptionCases() {
        {
            //test contains
            final LightWeightGSet<Integer, Integer> gset = new LightWeightGSet<Integer, Integer>(16);
            try {
                //test contains with a null element
                gset.contains(null);
                Assert.fail();
            } catch (NullPointerException e) {
                LOG.info("GOOD: getting " + e, e);
            }
        }

        {
            //test get
            final LightWeightGSet<Integer, Integer> gset = new LightWeightGSet<Integer, Integer>(16);
            try {
                //test get with a null element
                gset.get(null);
                Assert.fail();
            } catch (NullPointerException e) {
                LOG.info("GOOD: getting " + e, e);
            }
        }

        {
            //test put
            final LightWeightGSet<Integer, Integer> gset = new LightWeightGSet<Integer, Integer>(16);
            try {
                //test put with a null element
                gset.put(null);
                Assert.fail();
            } catch (NullPointerException e) {
                LOG.info("GOOD: getting " + e, e);
            }
            try {
                //test putting an element which is not implementing LinkedElement
                gset.put(1);
                Assert.fail();
            } catch (IllegalArgumentException e) {
                LOG.info("GOOD: getting " + e, e);
            }
        }

        {
            //test iterator
            final IntElement[] data = new IntElement[5];
            for (int i = 0; i < data.length; i++) {
                data[i] = new IntElement(i, i);
            }

            for (int v = 1; v < data.length - 1; v++) {
                {
                    //test remove while iterating
                    final GSet<IntElement, IntElement> gset = createGSet(data);
                    for (IntElement i : gset) {
                        if (i.value == v) {
                            //okay because data[0] is not in gset
                            gset.remove(data[0]);
                        }
                    }

                    try {
                        //exception because data[1] is in gset
                        for (IntElement i : gset) {
                            if (i.value == v) {
                                gset.remove(data[1]);
                            }
                        }
                        Assert.fail();
                    } catch (ConcurrentModificationException e) {
                        LOG.info("GOOD: getting " + e, e);
                    }
                }

                {
                    //test put new element while iterating
                    final GSet<IntElement, IntElement> gset = createGSet(data);
                    try {
                        for (IntElement i : gset) {
                            if (i.value == v) {
                                gset.put(data[0]);
                            }
                        }
                        Assert.fail();
                    } catch (ConcurrentModificationException e) {
                        LOG.info("GOOD: getting " + e, e);
                    }
                }

                {
                    //test put existing element while iterating
                    final GSet<IntElement, IntElement> gset = createGSet(data);
                    try {
                        for (IntElement i : gset) {
                            if (i.value == v) {
                                gset.put(data[3]);
                            }
                        }
                        Assert.fail();
                    } catch (ConcurrentModificationException e) {
                        LOG.info("GOOD: getting " + e, e);
                    }
                }
            }
        }
    }

    private static GSet<IntElement, IntElement> createGSet(final IntElement[] data) {
        final GSet<IntElement, IntElement> gset = new LightWeightGSet<IntElement, IntElement>(8);
        for (int i = 1; i < data.length; i++) {
            gset.put(data[i]);
        }
        return gset;
    }

    @Test
    public void testGSet() {
        //The parameters are: table length, data size, modulus.
        check(new GSetTestCase(1, 1 << 4, 65537));
        check(new GSetTestCase(17, 1 << 16, 17));
        check(new GSetTestCase(255, 1 << 10, 65537));
    }

    /**
     * A long running test with various data sets and parameters.
     * It may take ~5 hours, 
     * If you are changing the implementation,
     * please un-comment the following line in order to run the test.
     */
    //@Test
    public void runMultipleTestGSet() {
        for (int offset = -2; offset <= 2; offset++) {
            runTestGSet(1, offset);
            for (int i = 1; i < Integer.SIZE - 1; i++) {
                runTestGSet((1 << i) + 1, offset);
            }
        }
    }

    private static void runTestGSet(final int modulus, final int offset) {
        println("\n\nmodulus=" + modulus + ", offset=" + offset);
        for (int i = 0; i <= 16; i += 4) {
            final int tablelength = (1 << i) + offset;

            final int upper = i + 2;
            final int steps = Math.max(1, upper / 3);

            for (int j = 0; j <= upper; j += steps) {
                final int datasize = 1 << j;
                check(new GSetTestCase(tablelength, datasize, modulus));
            }
        }
    }

    private static void check(final GSetTestCase test) {
        //check add
        print("  check add .................. ");
        for (int i = 0; i < test.data.size() / 2; i++) {
            test.put(test.data.get(i));
        }
        for (int i = 0; i < test.data.size(); i++) {
            test.put(test.data.get(i));
        }
        println("DONE " + test.stat());

        //check remove and add
        print("  check remove & add ......... ");
        for (int j = 0; j < 10; j++) {
            for (int i = 0; i < test.data.size() / 2; i++) {
                final int r = ran.nextInt(test.data.size());
                test.remove(test.data.get(r));
            }
            for (int i = 0; i < test.data.size() / 2; i++) {
                final int r = ran.nextInt(test.data.size());
                test.put(test.data.get(r));
            }
        }
        println("DONE " + test.stat());

        //check remove
        print("  check remove ............... ");
        for (int i = 0; i < test.data.size(); i++) {
            test.remove(test.data.get(i));
        }
        Assert.assertEquals(0, test.gset.size());
        println("DONE " + test.stat());

        //check remove and add again
        print("  check remove & add again ... ");
        for (int j = 0; j < 10; j++) {
            for (int i = 0; i < test.data.size() / 2; i++) {
                final int r = ran.nextInt(test.data.size());
                test.remove(test.data.get(r));
            }
            for (int i = 0; i < test.data.size() / 2; i++) {
                final int r = ran.nextInt(test.data.size());
                test.put(test.data.get(r));
            }
        }
        println("DONE " + test.stat());

        final long s = (Time.now() - starttime) / 1000L;
        println("total time elapsed=" + s + "s\n");
    }

    /** Test cases */
    private static class GSetTestCase implements GSet<IntElement, IntElement> {
        final GSet<IntElement, IntElement> expected = new GSetByHashMap<IntElement, IntElement>(1024, 0.75f);
        final GSet<IntElement, IntElement> gset;
        final IntData data;

        final String info;
        final long starttime = Time.now();
        /** Determine the probability in {@link #check()}. */
        final int denominator;
        int iterate_count = 0;
        int contain_count = 0;

        GSetTestCase(int tablelength, int datasize, int modulus) {
            denominator = Math.min((datasize >> 7) + 1, 1 << 16);
            info = getClass().getSimpleName() + ": tablelength=" + tablelength + ", datasize=" + datasize
                    + ", modulus=" + modulus + ", denominator=" + denominator;
            println(info);

            data = new IntData(datasize, modulus);
            gset = new LightWeightGSet<IntElement, IntElement>(tablelength);

            Assert.assertEquals(0, gset.size());
        }

        private boolean containsTest(IntElement key) {
            final boolean e = expected.contains(key);
            Assert.assertEquals(e, gset.contains(key));
            return e;
        }

        @Override
        public boolean contains(IntElement key) {
            final boolean e = containsTest(key);
            check();
            return e;
        }

        private IntElement getTest(IntElement key) {
            final IntElement e = expected.get(key);
            Assert.assertEquals(e.id, gset.get(key).id);
            return e;
        }

        @Override
        public IntElement get(IntElement key) {
            final IntElement e = getTest(key);
            check();
            return e;
        }

        private IntElement putTest(IntElement element) {
            final IntElement e = expected.put(element);
            if (e == null) {
                Assert.assertEquals(null, gset.put(element));
            } else {
                Assert.assertEquals(e.id, gset.put(element).id);
            }
            return e;
        }

        @Override
        public IntElement put(IntElement element) {
            final IntElement e = putTest(element);
            check();
            return e;
        }

        private IntElement removeTest(IntElement key) {
            final IntElement e = expected.remove(key);
            if (e == null) {
                Assert.assertEquals(null, gset.remove(key));
            } else {
                Assert.assertEquals(e.id, gset.remove(key).id);
            }
            return e;
        }

        @Override
        public IntElement remove(IntElement key) {
            final IntElement e = removeTest(key);
            check();
            return e;
        }

        private int sizeTest() {
            final int s = expected.size();
            Assert.assertEquals(s, gset.size());
            return s;
        }

        @Override
        public int size() {
            final int s = sizeTest();
            check();
            return s;
        }

        @Override
        public Iterator<IntElement> iterator() {
            throw new UnsupportedOperationException();
        }

        void check() {
            //test size
            sizeTest();

            if (ran.nextInt(denominator) == 0) {
                //test get(..), check content and test iterator
                iterate_count++;
                for (IntElement i : gset) {
                    getTest(i);
                }
            }

            if (ran.nextInt(denominator) == 0) {
                //test contains(..)
                contain_count++;
                final int count = Math.min(data.size(), 1000);
                if (count == data.size()) {
                    for (IntElement i : data.integers) {
                        containsTest(i);
                    }
                } else {
                    for (int j = 0; j < count; j++) {
                        containsTest(data.get(ran.nextInt(data.size())));
                    }
                }
            }
        }

        String stat() {
            final long t = Time.now() - starttime;
            return String.format(" iterate=%5d, contain=%5d, time elapsed=%5d.%03ds", iterate_count, contain_count,
                    t / 1000, t % 1000);
        }

        @Override
        public void clear() {
            expected.clear();
            gset.clear();
            Assert.assertEquals(0, size());
        }
    }

    /** Test data set */
    private static class IntData {
        final IntElement[] integers;

        IntData(int size, int modulus) {
            integers = new IntElement[size];
            for (int i = 0; i < integers.length; i++) {
                integers[i] = new IntElement(i, ran.nextInt(modulus));
            }
        }

        IntElement get(int i) {
            return integers[i];
        }

        int size() {
            return integers.length;
        }
    }

    /** Elements of {@link LightWeightGSet} in this test */
    private static class IntElement implements LightWeightGSet.LinkedElement, Comparable<IntElement> {
        private LightWeightGSet.LinkedElement next;
        final int id;
        final int value;

        IntElement(int id, int value) {
            this.id = id;
            this.value = value;
        }

        @Override
        public boolean equals(Object obj) {
            return obj != null && obj instanceof IntElement && value == ((IntElement) obj).value;
        }

        @Override
        public int hashCode() {
            return value;
        }

        @Override
        public int compareTo(IntElement that) {
            return value - that.value;
        }

        @Override
        public String toString() {
            return id + "#" + value;
        }

        @Override
        public LightWeightGSet.LinkedElement getNext() {
            return next;
        }

        @Override
        public void setNext(LightWeightGSet.LinkedElement e) {
            next = e;
        }
    }

    /** 
     * Test for {@link LightWeightGSet#computeCapacity(double, String)}
     * with invalid percent less than 0.
     */
    @Test(expected = HadoopIllegalArgumentException.class)
    public void testComputeCapacityNegativePercent() {
        LightWeightGSet.computeCapacity(1024, -1.0, "testMap");
    }

    /** 
     * Test for {@link LightWeightGSet#computeCapacity(double, String)}
     * with invalid percent greater than 100.
     */
    @Test(expected = HadoopIllegalArgumentException.class)
    public void testComputeCapacityInvalidPercent() {
        LightWeightGSet.computeCapacity(1024, 101.0, "testMap");
    }

    /** 
     * Test for {@link LightWeightGSet#computeCapacity(double, String)}
     * with invalid negative max memory
     */
    @Test(expected = HadoopIllegalArgumentException.class)
    public void testComputeCapacityInvalidMemory() {
        LightWeightGSet.computeCapacity(-1, 50.0, "testMap");
    }

    private static boolean isPowerOfTwo(int num) {
        return num == 0 || (num > 0 && Integer.bitCount(num) == 1);
    }

    /** Return capacity as percentage of total memory */
    private static int getPercent(long total, int capacity) {
        // Reference size in bytes
        double referenceSize = System.getProperty("sun.arch.data.model").equals("32") ? 4.0 : 8.0;
        return (int) (((capacity * referenceSize) / total) * 100.0);
    }

    /** Return capacity as percentage of total memory */
    private static void testCapacity(long maxMemory, double percent) {
        int capacity = LightWeightGSet.computeCapacity(maxMemory, percent, "map");
        LOG.info("Validating - total memory " + maxMemory + " percent " + percent + " returned capacity "
                + capacity);
        // Returned capacity is zero or power of two
        Assert.assertTrue(isPowerOfTwo(capacity));

        // Ensure the capacity returned is the nearest to the asked perecentage
        int capacityPercent = getPercent(maxMemory, capacity);
        if (capacityPercent == percent) {
            return;
        } else if (capacityPercent > percent) {
            Assert.assertTrue(getPercent(maxMemory, capacity * 2) > percent);
        } else {
            Assert.assertTrue(getPercent(maxMemory, capacity / 2) < percent);
        }
    }

    /** 
     * Test for {@link LightWeightGSet#computeCapacity(double, String)}
     */
    @Test
    public void testComputeCapacity() {
        // Tests for boundary conditions where percent or memory are zero
        testCapacity(0, 0.0);
        testCapacity(100, 0.0);
        testCapacity(0, 100.0);

        // Compute capacity for some 100 random max memory and percentage
        Random r = new Random();
        for (int i = 0; i < 100; i++) {
            long maxMemory = r.nextInt(Integer.MAX_VALUE);
            double percent = r.nextInt(101);
            testCapacity(maxMemory, percent);
        }
    }
}