/* * Copyright (C) 2015 Red Hat, Inc. and/or its affiliates. * * 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.jboss.errai.marshalling.tests; import java.io.File; import java.lang.reflect.Array; import java.math.BigDecimal; import java.math.BigInteger; import java.math.MathContext; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Random; import java.util.Set; import java.util.TreeMap; import java.util.TreeSet; import junit.framework.Assert; import org.jboss.errai.marshalling.client.MarshallingSessionProviderFactory; import org.jboss.errai.marshalling.client.api.Marshaller; import org.jboss.errai.marshalling.client.api.MarshallingSession; import org.jboss.errai.marshalling.client.api.ParserFactory; import org.jboss.errai.marshalling.client.api.json.EJValue; import org.jboss.errai.marshalling.server.MappingContextSingleton; import org.jboss.errai.marshalling.server.ServerMarshalling; import org.jboss.errai.marshalling.tests.res.EntityWithInheritedTypeVariable; import org.jboss.errai.marshalling.tests.res.EnumContainer; import org.jboss.errai.marshalling.tests.res.EnumContainerContainer; import org.jboss.errai.marshalling.tests.res.EnumTestA; import org.jboss.errai.marshalling.tests.res.EnumWithAbstractMethod; import org.jboss.errai.marshalling.tests.res.EnumWithState; import org.jboss.errai.marshalling.tests.res.ImmutableEnumContainer; import org.jboss.errai.marshalling.tests.res.Outer; import org.jboss.errai.marshalling.tests.res.Outer2; import org.jboss.errai.marshalling.tests.res.SType; import org.jboss.errai.marshalling.tests.res.shared.Role; import org.jboss.errai.marshalling.tests.res.shared.User; import org.junit.Test; /** * @author Mike Brock <cbrock@redhat.com> */ public class ServerMarshallingTest { static { System.setProperty("errai.devel.nocache", "true"); System.out.println("Working Dir: " + new File("").getAbsoluteFile().getAbsolutePath()); } @SuppressWarnings("unchecked") private void testEncodeDecodeDynamic(Object value) { if (value == null) return; testEncodeDecode((Class<Object>) value.getClass(), value); } private <T> void testEncodeDecode(Class<T> type, T value) { Marshaller<Object> marshaller = MappingContextSingleton.get().getMarshaller(type.getName()); Assert.assertNotNull("did not find " + type.getName() + " marshaller", marshaller); MarshallingSession encSession = MarshallingSessionProviderFactory.getEncoding(); String enc = "[" + marshaller.marshall(value, encSession) + "]"; MarshallingSession decSession = MarshallingSessionProviderFactory.getDecoding(); EJValue parsedJson = ParserFactory.get().parse(enc); Assert.assertTrue("expected outer JSON to be array", parsedJson.isArray() != null); EJValue encodedNode = parsedJson.isArray().get(0); Object dec = marshaller.demarshall(encodedNode, decSession); Assert.assertTrue("decoded type not an instance of " + value.getClass(), type.isAssignableFrom(value.getClass())); assertEquals(value, dec); } private static void assertEquals(Object a1, Object a2) { if (a1 != null && a2 != null) { if (a1.getClass().isArray()) { if (a2.getClass().isArray()) { assertArrayEquals(a1, a2); return; } } } Assert.assertEquals(a1, a2); } private static void assertArrayEquals(Object array1, Object array2) { int len1 = Array.getLength(array1); int len2 = Array.getLength(array2); if (len1 != len2) Assert.failNotEquals("different length arrays!", array1, array2); Object el1, el2; for (int i = 0; i < len1; i++) { el1 = Array.get(array1, i); el2 = Array.get(array2, i); if ((el1 == null || el2 == null) && el1 != null) { Assert.failNotEquals("different values", array1, array2); } else if (el1 != null) { assertEquals(el1, el2); } } } /** * Tests that the MappingContext.getMarshaller() returns null when asked for a * marshaller it doesn't have. In particular, it should not throw an exception. */ @Test public void testMissingTypeGetsNullMarshaller() { Marshaller<Object> marshaller = MappingContextSingleton.get().getMarshaller("does.not.Exist"); Assert.assertNull(marshaller); } @Test public void testString() { testEncodeDecode(String.class, "ThisIsOurTestString"); } @Test public void testStringEncodeWithHighLevelAPI() { final String val = "Seventeen-oh-one"; String json = ServerMarshalling.toJSON(val); assertEquals("\"Seventeen-oh-one\"", json); } /** * This method tests for string round-trip encode/decode using the * ServerMarshalling.toJSON() and ServerMarshalling.fromJSON() methods * specifically. The success or failure of this method is not predicted by the * success or failure of {@link #testString()}, which uses lower-level APIs. */ @Test public void testStringRoundTripWithHighLevelAPI() { final String val = "Seventeen-oh-one"; String json = ServerMarshalling.toJSON(val); Assert.assertEquals("Failed to round-trip the string", val, ServerMarshalling.fromJSON(json)); } /** * Tests that strings containing non-ASCII characters survive the * encode/decode process. */ @Test public void testNonAsciiString() { testEncodeDecode(String.class, "S\u00ebvent\u00e9\u00ebn-\u00f8h-\u00f3\u00f1e"); } @Test public void testEscapesInString() { testEncodeDecode(String.class, "\n\t\r\n{}{}{}\\}\\{\\]\\["); } @Test public void testStringArray() { testEncodeDecode(String[].class, new String[]{"foo", "bar", "superfoobar", "ultrafoobar"}); } @Test public void testIntegerArray() { testEncodeDecode(Integer[].class, new Integer[]{1, 2, 3, 4, 5, 6, 7, 8, 9}); } @Test public void testPrimIntegerArray() { testEncodeDecode(int[].class, new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9}); } @Test public void testLongArray() { testEncodeDecode(Long[].class, new Long[]{1l, 2l, 3l, 4l, 5l, 6l, 7l, 8l, 9l}); } @Test public void testPrimLongArray() { testEncodeDecode(long[].class, new long[]{1l, 2l, 3l, 4l, 5l, 6l, 7l, 8l, 9l}); } @Test public void testIntegerMaxValue() { testEncodeDecode(Integer.class, Integer.MAX_VALUE); } @Test public void testIntegerMinValue() { testEncodeDecode(Integer.class, Integer.MIN_VALUE); } @Test public void testIntegerRandomValue() { testEncodeDecode(Integer.class, new Random(System.currentTimeMillis()).nextInt(Integer.MAX_VALUE)); } @Test public void testShortMinValue() { testEncodeDecode(Short.class, Short.MAX_VALUE); } @Test public void testShortMaxValue() { testEncodeDecode(Short.class, Short.MIN_VALUE); } @Test public void testShortRandomValue() { testEncodeDecode(Short.class, (short) new Random(System.currentTimeMillis()).nextInt(Short.MAX_VALUE)); } @Test public void testLongMaxValue() { testEncodeDecode(Long.class, Long.MAX_VALUE); } @Test public void testLongMinValue() { testEncodeDecode(Long.class, Long.MIN_VALUE); } @Test public void testLong6536000376648360988() { testEncodeDecode(Long.class, 6536000376648360988L); } @Test public void testLongRandomValue() { testEncodeDecode(Long.class, new Random(System.currentTimeMillis()).nextLong()); } @Test public void testDoubleMaxValue() { testEncodeDecode(Double.class, Double.MAX_VALUE); } @Test public void testDoubleMinValue() { testEncodeDecode(Double.class, Double.MIN_VALUE); } @Test public void testDoubleRandomValue() { testEncodeDecode(Double.class, new Random(System.currentTimeMillis()).nextDouble()); } @Test public void testDouble0dot9635950160419999() { testEncodeDecode(Double.class, 0.9635950160419999d); } @Test public void testDoubleNan() { testEncodeDecode(Double.class, Double.NaN); } @Test public void testDoublePosInf() { testEncodeDecode(Double.class, Double.POSITIVE_INFINITY); } @Test public void testDoubleNegInf() { testEncodeDecode(Double.class, Double.NEGATIVE_INFINITY); } @Test public void testFloatMaxValue() { testEncodeDecode(Float.class, Float.MAX_VALUE); } @Test public void testFloatMinValue() { testEncodeDecode(Float.class, Float.MIN_VALUE); } @Test public void testFloatRandomValue() { testEncodeDecode(Float.class, new Random(System.currentTimeMillis()).nextFloat()); } @Test public void testFloatNan() { testEncodeDecode(Float.class, Float.NaN); } @Test public void testFloatPosInf() { testEncodeDecode(Float.class, Float.POSITIVE_INFINITY); } @Test public void testFloatNegInf() { testEncodeDecode(Float.class, Float.NEGATIVE_INFINITY); } @Test public void testByteMaxValue() { testEncodeDecode(Byte.class, Byte.MAX_VALUE); } @Test public void testByteMinValue() { testEncodeDecode(Byte.class, Byte.MIN_VALUE); } @Test public void testByteRandomValue() { testEncodeDecode(Byte.class, (byte) new Random(System.currentTimeMillis()).nextInt()); } @Test public void testBooleanTrue() { testEncodeDecode(Boolean.class, Boolean.TRUE); } @Test public void testBooleanFalse() { testEncodeDecode(Boolean.class, Boolean.FALSE); } @Test public void testCharMaxValue() { testEncodeDecode(Character.class, Character.MAX_VALUE); } @Test public void testCharMinValue() { testEncodeDecode(Character.class, Character.MIN_VALUE); } @Test public void testListMarshall() { testEncodeDecodeDynamic(Arrays.asList("foo", "bar", "sillyhat")); } @Test public void testUnmodifiableListMarshall() { testEncodeDecodeDynamic(Collections.unmodifiableList(Arrays.asList("foo", "bar", "sillyhat"))); } @Test public void testUnmodifiableSetMarshall() { testEncodeDecodeDynamic(Collections.unmodifiableSet(new HashSet<String>(Arrays.asList("foo", "bar", "sillyhat")))); } @Test public void testUnmodifiableSortedSetMarshall() { testEncodeDecodeDynamic(Collections.unmodifiableSortedSet(new TreeSet<String>(Arrays.asList("foo", "bar", "sillyhat")))); } @Test public void testSingletonListMarshall() { testEncodeDecodeDynamic(Collections.singletonList("foobie")); } @Test public void testSetMarshall() { testEncodeDecodeDynamic(new HashSet<String>(Arrays.asList("foo", "bar", "sillyhat"))); } @Test public void testEmptyList() { testEncodeDecodeDynamic(Collections.emptyList()); } @Test public void testEmptySet() { testEncodeDecodeDynamic(Collections.emptySet()); } @Test public void testEmptyMap() { testEncodeDecodeDynamic(Collections.emptyMap()); } @Test public void testMapWithBigIntegerAsKey() { Map<BigInteger, String> map = new HashMap<BigInteger, String>(); map.put(new BigInteger("10"), "10 value"); testEncodeDecodeDynamic(map); } @Test public void testMapWithBigDecimalAsKey() { Map<BigDecimal, String> map = new HashMap<BigDecimal, String>(); map.put(new BigDecimal("10"), "10 value"); testEncodeDecodeDynamic(map); } @Test public void testSynchronizedSortedMap() { TreeMap<String, String> map = new TreeMap<String, String>(); map.put("a", "a"); map.put("b", "b"); map.put("c", "c"); testEncodeDecodeDynamic(Collections.synchronizedSortedMap(map)); } @Test public void testSynchronizedMap() { HashMap<String, String> map = new HashMap<String, String>(); map.put("a", "a"); map.put("b", "b"); map.put("c", "c"); testEncodeDecodeDynamic(Collections.synchronizedMap(map)); } @Test public void testSynchronizedSortedSet() { TreeSet<String> set = new TreeSet<String>(); set.add("a"); set.add("b"); set.add("c"); testEncodeDecodeDynamic(Collections.synchronizedSortedSet(set)); } @Test public void testSynchronizedSet() { HashSet<String> set = new HashSet<String>(); set.add("a"); set.add("b"); set.add("c"); testEncodeDecodeDynamic(Collections.synchronizedSet(set)); } @Test public void testUserEntity() { User user = new User(); user.setUserName("foo"); user.setPassword("bar"); Set<Role> roles = new HashSet<Role>(); roles.add(new Role("admin")); roles.add(new Role("users")); user.setRoles(roles); testEncodeDecodeDynamic(user); } class ServerRandomProvider implements RandomProvider { private final char[] CHARS = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0'}; private final Random random = new Random(System.nanoTime()); @Override public boolean nextBoolean() { return random.nextBoolean(); } @Override public int nextInt(int upper) { return random.nextInt(upper); } @Override public double nextDouble() { return new BigDecimal(random.nextDouble(), MathContext.DECIMAL32).doubleValue(); } @Override public char nextChar() { return CHARS[nextInt(1000) % CHARS.length]; } @Override public String randString() { StringBuilder builder = new StringBuilder(); int len = nextInt(25) + 5; for (int i = 0; i < len; i++) { builder.append(nextChar()); } return builder.toString(); } } public interface RandomProvider { public boolean nextBoolean(); public int nextInt(int upper); public double nextDouble(); public char nextChar(); public String randString(); } @Test public void testSTypeEntity() { SType sType = SType.create(new ServerRandomProvider()); // long st = System.currentTimeMillis(); // for (int i = 0; i < 10000; i++) { testEncodeDecodeDynamic(sType); MappingContextSingleton.get(); String json = ServerMarshalling.toJSON(sType); System.out.println(json); // } // System.out.println(System.currentTimeMillis() - st); } @Test public void testPrimitiveIntRoundTrip() { final int val = 1701; String json = ServerMarshalling.toJSON(val); Assert.assertEquals("Failed to marshall/demarshall int", val, ServerMarshalling.fromJSON(json)); } @Test public void testPrimitiveLongRoundTrip() { final long val = 1701l; String json = ServerMarshalling.toJSON(val); Assert.assertEquals("Failed to marshall/demarshall long", val, ServerMarshalling.fromJSON(json)); } @Test public void testPrimitiveDoubleRoundTrip() { final double val = 17.01; String json = ServerMarshalling.toJSON(val); Assert.assertEquals("Failed to marshall/demarshall double", val, ServerMarshalling.fromJSON(json)); } @Test public void testPrimitiveFloatRoundTrip() { final float val = 1701f; String json = ServerMarshalling.toJSON(val); Assert.assertEquals("Failed to marshall/demarshall float", val, ServerMarshalling.fromJSON(json)); } @Test public void testSimpleEnumRoundTrip() { EnumTestA val = EnumTestA.FIRST; String json = ServerMarshalling.toJSON(val); Assert.assertEquals("Failed to marshall/demarshall enum", val, ServerMarshalling.fromJSON(json)); } @Test public void testEnumContainerWithNullRefs() { EnumContainer val = new EnumContainer(); String json = ServerMarshalling.toJSON(val); Assert.assertEquals("Failed to marshall/demarshall enum container with nulls", val.toString(), ServerMarshalling.fromJSON(json).toString()); } @Test public void testEnumContainerWithDistinctRefs() { EnumContainer val = new EnumContainer(); val.setEnumA1(EnumTestA.FIRST); val.setEnumA2(EnumTestA.SECOND); val.setEnumA3(EnumTestA.THIRD); val.setStatefulEnum1(EnumWithState.THING1); val.setStatefulEnum2(EnumWithState.THING2); String json = ServerMarshalling.toJSON(val); Assert.assertEquals("Failed to marshall/demarshall enum container with distinct refs", val.toString(), ServerMarshalling.fromJSON(json).toString()); } @Test public void testEnumContainerWithRepeatedRefs() { EnumContainer val = new EnumContainer(); val.setEnumA1(EnumTestA.FIRST); val.setEnumA2(EnumTestA.FIRST); val.setEnumA3(EnumTestA.FIRST); val.setStatefulEnum1(EnumWithState.THING1); val.setStatefulEnum2(EnumWithState.THING1); val.setStatefulEnum3(EnumWithState.THING1); String json = ServerMarshalling.toJSON(val); Assert.assertEquals("Failed to marshall/demarshall enum container with repeated refs", val.toString(), ServerMarshalling.fromJSON(json).toString()); } @Test public void testEnumContainerContainer() { EnumContainerContainer val = new EnumContainerContainer(); EnumContainer enumContainer = new EnumContainer(); enumContainer.setEnumA1(EnumTestA.FIRST); val.setEnumContainer(enumContainer); val.setEnumA(EnumTestA.FIRST); String json = ServerMarshalling.toJSON(val); System.out.println(json); Assert.assertEquals("Failed to marshall/demarshall enum container container", val.toString(), ServerMarshalling.fromJSON(json).toString()); } @Test // This tests guards against regressions of https://issues.jboss.org/browse/ERRAI-370 public void testEnumWithAbstractMethod() { EnumWithAbstractMethod val = EnumWithAbstractMethod.THING2; String json = ServerMarshalling.toJSON(val); Assert.assertEquals("Failed to marshall/demarshall enum with abstract method", val, ServerMarshalling.fromJSON(json)); } @Test public void testImmutableEnumContainer() { ImmutableEnumContainer val = new ImmutableEnumContainer(EnumTestA.FIRST); String json = ServerMarshalling.toJSON(val); Assert.assertEquals("Failed to marshall/demarshall enum container", val, ServerMarshalling.fromJSON(json)); } @Test public void testImmutableEnumContainerWithNullRefs() { ImmutableEnumContainer val = new ImmutableEnumContainer(null); String json = ServerMarshalling.toJSON(val); Assert.assertEquals("Failed to marshall/demarshall immutable enum container with nulls", val, ServerMarshalling.fromJSON(json)); } @Test public void testListWithInheritedTypeVariable() { EntityWithInheritedTypeVariable<String> val = new EntityWithInheritedTypeVariable<String>(); val.setList(Arrays.asList("one", "gwt", null)); val.addToFieldAccessedList("this is an entry"); String json = ServerMarshalling.toJSON(val); Assert.assertEquals("Failed to marshall/demarshall immutable enum container with nulls", val, ServerMarshalling.fromJSON(json)); } // This is a regression test for ERRAI-794 @Test public void testBackReferenceOrderingWithMapsTo() { Outer.Nested key = new Outer.Nested("exp"); Outer outer = new Outer (Arrays.asList(key), key); testEncodeDecode(Outer.class, outer); Outer2.Nested key2 = new Outer2.Nested("exp"); Outer2 outer2 = new Outer2 (key2, Arrays.asList(key2)); testEncodeDecode(Outer2.class, outer2); } }