/**
* Copyright (C) 2012-2013 Selventa, Inc.
*
* This file is part of the OpenBEL Framework.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The OpenBEL Framework is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the OpenBEL Framework. If not, see <http://www.gnu.org/licenses/>.
*
* Additional Terms under LGPL v3:
*
* This license does not authorize you and you are prohibited from using the
* name, trademarks, service marks, logos or similar indicia of Selventa, Inc.,
* or, in the discretion of other licensors or authors of the program, the
* name, trademarks, service marks, logos or similar indicia of such authors or
* licensors, in any marketing or advertising materials relating to your
* distribution of the program or any covered product. This restriction does
* not waive or limit your obligation to keep intact all copyright notices set
* forth in the program as delivered to you.
*
* If you distribute the program in whole or in part, or any modified version
* of the program, and you assume contractual liability to the recipient with
* respect to the program or modified version, then you will indemnify the
* authors and licensors of the program for any liabilities that these
* contractual assumptions directly impose on those licensors and authors.
*/
package org.openbel.framework.ws.utils;
import static java.lang.String.format;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
import java.util.Arrays;
import java.util.Random;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.openbel.framework.api.KamStoreObject;
import org.openbel.framework.ws.utils.Converter.KamStoreObjectRef;
public class TestConverter {
private static final long SEED = 71073753702L;
private static final Random random = new Random(SEED);
// Reflected from Converter
private static Charset ASCII;
private static Method typeEncode;
// Reflected from Converter.KamStoreObjectType
private static Class<?> kamStoreObjectTypeClass;
private static String kamStoreObjectTypeClassName;
private static Field repField;
private static Method sortAndCreateIndex, getRepresentation,
getRepresentedClass;
private byte[] representations;
private int[] repIndex;
// Reflected from Converter.KamStoreObjectRef
private static Method encode, decode;
@BeforeClass
public static void setUpBeforeClass() throws Throwable {
final Field asciiField = Converter.class.getDeclaredField("ASCII");
asciiField.setAccessible(true);
ASCII = (Charset) asciiField.get(null);
kamStoreObjectTypeClass = null;
Class<?>[] converterInnerClasses = Converter.class.getDeclaredClasses();
for (int i = 0; i < converterInnerClasses.length; ++i) {
final Class<?> converterInnerClass = converterInnerClasses[i];
if (converterInnerClass.getSimpleName()
.equals("KamStoreObjectType")) {
kamStoreObjectTypeClass = converterInnerClass;
break;
}
}
assertThat(kamStoreObjectTypeClass, is(not(nullValue())));
kamStoreObjectTypeClassName = kamStoreObjectTypeClass.getSimpleName();
repField = kamStoreObjectTypeClass.getDeclaredField("rep");
repField.setAccessible(true);
getRepresentation =
kamStoreObjectTypeClass.getDeclaredMethod("getRepresentation");
getRepresentedClass =
kamStoreObjectTypeClass
.getDeclaredMethod("getRepresentedClass");
sortAndCreateIndex = kamStoreObjectTypeClass.getDeclaredMethod(
"sortAndCreateIndex", byte[].class);
sortAndCreateIndex.setAccessible(true);
typeEncode =
Converter.class.getDeclaredMethod("encode",
CharsetEncoder.class, char.class);
typeEncode.setAccessible(true);
encode =
KamStoreObjectRef.class.getDeclaredMethod("encode", int.class,
int.class, byte.class);
encode.setAccessible(true);
decode =
KamStoreObjectRef.class.getDeclaredMethod("decode",
String.class, kamStoreObjectTypeClass);
decode.setAccessible(true);
}
@Before
public void setup() throws Throwable {
try {
final Field representationsField =
kamStoreObjectTypeClass.getDeclaredField("representations");
representationsField.setAccessible(true);
representations = (byte[]) representationsField.get(null);
final Field repIndexField =
kamStoreObjectTypeClass.getDeclaredField("repIndex");
repIndexField.setAccessible(true);
repIndex = (int[]) repIndexField.get(null);
} catch (ExceptionInInitializerError e) {
throw e.getCause();
}
}
@SuppressWarnings("unchecked")
@Test
public void testThatDecodeEncodeIsIdentity() throws Throwable {
try {
for (Object type : kamStoreObjectTypeClass.getEnumConstants()) {
int kamInfoId = random.nextInt();
int id = 0;
// A valid ID is not less than 1
while ((id = random.nextInt()) < 1) {}
final byte prefix =
((Byte) getRepresentation.invoke(type)).byteValue();
final Class<? extends KamStoreObject> typeClass =
(Class<? extends KamStoreObject>) getRepresentedClass
.invoke(type);
final String encoded =
(String) encode.invoke(null, kamInfoId, id, prefix);
final KamStoreObjectRef objRef =
(KamStoreObjectRef) decode.invoke(
null, encoded, type);
assertTrue(kamInfoId == objRef.getKamInfoId());
assertTrue(id == objRef.getKamStoreObjectId());
assertTrue(typeClass.equals(objRef.getKamStoreObjectClass()));
}
} catch (InvocationTargetException e) {
throw e.getCause();
}
}
@Test
public void testThatKamStoreObjectTypeRepsCanBeEncoded()
throws IllegalAccessException,
IllegalArgumentException, InvocationTargetException {
final CharsetEncoder asciiEncoder = ASCII.newEncoder();
final int n = kamStoreObjectTypeClass.getEnumConstants().length;
for (int i = 0; i < n; ++i) {
Enum<?> type =
(Enum<?>) kamStoreObjectTypeClass.getEnumConstants()[i];
Byte rep =
(Byte) typeEncode.invoke(null, asciiEncoder,
repField.getChar(type));
if (rep == null) {
fail(format(
"%s.%s must have a one-byte ASCII character representation!",
kamStoreObjectTypeClassName,
type.name()));
}
}
}
@Test
public void testThatKamStoreObjectTypeRepsAreUnique()
throws IllegalArgumentException,
IllegalAccessException {
final int n = kamStoreObjectTypeClass.getEnumConstants().length;
for (int i = 1; i < n; ++i) {
if (representations[i - 1] == representations[i]) {
final Enum<?> prevType = (Enum<?>) kamStoreObjectTypeClass
.getEnumConstants()[repIndex[i - 1]];
final Enum<?> type = (Enum<?>) kamStoreObjectTypeClass
.getEnumConstants()[repIndex[i]];
fail(format(
"%s.%s and %s.%s have the same single character representation '%c'!",
kamStoreObjectTypeClassName,
prevType.name(),
kamStoreObjectTypeClassName,
type.name(),
repField.getChar(type)));
}
}
}
@Test
public void testSortAndCreateIndex() throws IllegalAccessException,
IllegalArgumentException, InvocationTargetException {
final int n = kamStoreObjectTypeClass.getEnumConstants().length;
byte[] bytes = new byte[n];
random.nextBytes(bytes);
byte[] copy1 = Arrays.copyOf(bytes, n);
byte[] copy2 = Arrays.copyOf(bytes, n);
// Sort with sortAndCreateIndex and Arrays.sort.
int[] index = (int[]) sortAndCreateIndex.invoke(null, copy1);
Arrays.sort(copy2);
assertTrue(Arrays.equals(copy1, copy2));
// Check the index
for (int i = 0; i < n; ++i) {
assertTrue(bytes[index[i]] == copy1[i]);
}
}
}