/**
* 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.hdfs.util;
import org.apache.hadoop.util.Time;
import org.junit.Assert;
import org.junit.Test;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.Random;
public class TestGSet {
private static final Random ran = new Random();
private static final long starttime = Time.now();
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) {
LightWeightGSet.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) {
LightWeightGSet.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) {
LightWeightGSet.LOG.info("GOOD: getting " + e, e);
}
try {
//test putting an element which is not implementing LinkedElement
gset.put(1);
Assert.fail();
} catch (IllegalArgumentException e) {
LightWeightGSet.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) {
LightWeightGSet.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) {
LightWeightGSet.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) {
LightWeightGSet.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 test,
* which may take ~5 hours,
* with various data sets and parameters.
* 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);
}
check();
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);
}
}
/**
* 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;
}
}
}