/* * Copyright 2011 Google 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 com.google.gwt.user.client.rpc; import com.google.gwt.event.shared.UmbrellaException; import com.google.gwt.user.client.rpc.TestSetFactory.MarkerTypeEmptyKey; import com.google.gwt.user.client.rpc.TestSetFactory.MarkerTypeEmptyList; import com.google.gwt.user.client.rpc.TestSetFactory.MarkerTypeEmptySet; import com.google.gwt.user.client.rpc.TestSetFactory.MarkerTypeEmptyValue; import com.google.gwt.user.client.rpc.TestSetFactory.MarkerTypeEnum; import com.google.gwt.user.client.rpc.TestSetFactory.MarkerTypeIdentityHashMapKey; import com.google.gwt.user.client.rpc.TestSetFactory.MarkerTypeIdentityHashMapValue; import com.google.gwt.user.client.rpc.TestSetFactory.MarkerTypeSingleton; import com.google.gwt.user.client.rpc.TestSetFactory.MarkerTypeTreeMap; import com.google.gwt.user.client.rpc.TestSetFactory.MarkerTypeTreeSet; import com.google.gwt.user.client.rpc.TestSetFactory.SerializableDoublyLinkedNode; import com.google.gwt.user.client.rpc.TestSetFactory.SerializableGraphWithCFS; import com.google.gwt.user.client.rpc.TestSetFactory.SerializablePrivateNoArg; import com.google.gwt.user.client.rpc.TestSetFactory.SerializableWithTwoArrays; import com.google.gwt.user.client.rpc.RecursiveClassTestService.ResultNode; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertNotNull; import static junit.framework.Assert.assertSame; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.IdentityHashMap; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeMap; import java.util.TreeSet; import java.util.Vector; import java.util.Map.Entry; /** * Misnamed set of static validation methods used by various collection class * tests. * <p> * TODO: could add generics to require args to be of the same type */ public class TestSetValidator { private static class UnassignableObject { } /** * Check that an Object array has it's meta-data preserved (e.g. Array.queryId), * to ensure proper type checking on element assignment. */ public static boolean checkObjectArrayElementAssignment(Object[] array, int index, Object value) { // first check that the requested assignment succeeds try { array[index] = value; } catch (ArrayStoreException e) { return false; } // next check that assignment with a bogus type throws ArrayStoreException try { array[index] = new UnassignableObject(); return false; } catch (ArrayStoreException e) { return true; } } public static boolean equals(boolean[] expected, boolean[] actual) { if (actual == null) { return false; } if (expected.length != actual.length) { return false; } for (int i = 0; i < expected.length; ++i) { if (expected[i] != actual[i]) { return false; } } return true; } public static boolean equals(byte[] expected, byte[] actual) { if (actual == null) { return false; } if (actual.length != expected.length) { return false; } for (int i = 0; i < expected.length; ++i) { if (expected[i] != actual[i]) { return false; } } return true; } public static boolean equals(char[] expected, char[] actual) { if (actual == null) { return false; } if (actual.length != expected.length) { return false; } for (int i = 0; i < expected.length; ++i) { if (expected[i] != actual[i]) { return false; } } return true; } public static boolean equals(double[] expected, double[] actual) { if (actual == null) { return false; } if (actual.length != expected.length) { return false; } for (int i = 0; i < expected.length; ++i) { if (expected[i] != actual[i]) { return false; } } return true; } public static boolean equals(float[] expected, float[] actual) { if (actual == null) { return false; } if (actual.length != expected.length) { return false; } for (int i = 0; i < expected.length; ++i) { if (expected[i] != actual[i]) { return false; } } return true; } public static boolean equals(int[] expected, int[] actual) { if (actual == null) { return false; } if (actual.length != expected.length) { return false; } for (int i = 0; i < expected.length; ++i) { if (expected[i] != actual[i]) { return false; } } return true; } public static boolean equals(Iterator<?> expected, Iterator<?> actual) { while (expected.hasNext() && actual.hasNext()) { if (!expected.next().equals(actual.next())) { return false; } } return expected.hasNext() == actual.hasNext(); } public static boolean equals(long[] expected, long[] actual) { if (actual == null) { return false; } if (actual.length != expected.length) { return false; } for (int i = 0; i < expected.length; ++i) { if (expected[i] != actual[i]) { return false; } } return true; } public static boolean equals(Object[] o1, Object[] o2) { if (o1 == o2) { return true; } if (o1 == null || o2 == null) { return false; } if (o1.length != o2.length) { return false; } for (int i = 0; i < o1.length; ++i) { Object e1 = o1[i]; Object e2 = o2[i]; if (e1 == e2) { continue; } if (e1 == null || e2 == null) { return false; } if (!e1.equals(e2)) { return false; } } return true; } public static boolean equals(short[] expected, short[] actual) { if (actual == null) { return false; } if (actual.length != expected.length) { return false; } for (int i = 0; i < expected.length; ++i) { if (expected[i] != actual[i]) { return false; } } return true; } private static boolean equals(Throwable expected, Throwable actual) { // If one is null, both must be null (or vice-versa) if ((expected == null) != (actual == null)) { return false; } // Only check expected for null, as we know that actual is the same. if (actual == null) { return false; } String expectedMessage = expected.getMessage(); String actualMessage = actual.getMessage(); if (!equalsWithNullCheck(expectedMessage, actualMessage)) { return false; } /* * The cause field is not serialized, so we cannot verify it. */ /* * Stack traces are not comparable because they are automatically filled in when * the exception is instantiated, with the instantiation site's stack trace. */ return true; } public static boolean isValid(ArrayList<?> list) { if (list == null) { return false; } ArrayList<?> reference = TestSetFactory.createArrayList(); if (reference.size() != list.size()) { return false; } return reference.equals(list); } public static boolean isValid(List<MarkerTypeEmptyList> list) { return list != null && list.size() == 0; } public static boolean isValid(Map<MarkerTypeEmptyKey, MarkerTypeEmptyValue> map) { return map != null && map.size() == 0; } public static boolean isValid(Set<MarkerTypeEmptySet> set) { return set != null && set.size() == 0; } public static boolean isValid(HashMap<?, ?> expected, HashMap<?, ?> map) { if (map == null) { return false; } if (expected.size() != map.size()) { return false; } Set<?> entries = expected.entrySet(); Iterator<?> entryIter = entries.iterator(); while (entryIter.hasNext()) { Entry<?, ?> entry = (Entry<?, ?>) entryIter.next(); Object value = map.get(entry.getKey()); if (value != entry.getValue()) { if (value == null || entry.getValue() == null) { return false; } if (!map.get(entry.getKey()).equals(entry.getValue())) { return false; } } } return true; } public static boolean isValid(HashSet<?> expected, HashSet<?> actual) { if (actual == null) { return false; } if (expected.size() != actual.size()) { return false; } Iterator<?> entryIter = expected.iterator(); while (entryIter.hasNext()) { Object entry = entryIter.next(); if (!actual.contains(entry)) { return false; } } return true; } public static boolean isValidEnumKey( IdentityHashMap<MarkerTypeEnum, MarkerTypeIdentityHashMapValue> expected, IdentityHashMap<MarkerTypeEnum, MarkerTypeIdentityHashMapValue> map) { if (map == null) { return false; } if (expected.size() != map.size()) { return false; } Set<?> entries = expected.entrySet(); Iterator<?> entryIter = entries.iterator(); while (entryIter.hasNext()) { Entry<?, ?> entry = (Entry<?, ?>) entryIter.next(); Object value = map.get(entry.getKey()); if (value != entry.getValue()) { if (value == null || entry.getValue() == null) { return false; } if (!map.get(entry.getKey()).equals(entry.getValue())) { return false; } } } return true; } public static boolean isValid( IdentityHashMap<MarkerTypeIdentityHashMapKey, MarkerTypeIdentityHashMapValue> expected, IdentityHashMap<MarkerTypeIdentityHashMapKey, MarkerTypeIdentityHashMapValue> map) { if (map == null) { return false; } if (expected.size() != map.size()) { return false; } Set<Entry<MarkerTypeIdentityHashMapKey, MarkerTypeIdentityHashMapValue>> mapEntries = map.entrySet(); Set<Entry<MarkerTypeIdentityHashMapKey, MarkerTypeIdentityHashMapValue>> expectedEntries = expected.entrySet(); /* * An IdentityHashMap uses reference equality for keys. The maps we are * comparing have keys from different sources, so we cannot simply use get * to find one key in the other map. Instead we need to iterate looking for * equality. We do not check for null keys/value; rather, catch them with * NPEs. */ Iterator<Entry<MarkerTypeIdentityHashMapKey, MarkerTypeIdentityHashMapValue>> expectedIter = expectedEntries.iterator(); while (expectedIter.hasNext()) { Entry<MarkerTypeIdentityHashMapKey, MarkerTypeIdentityHashMapValue> expectedEntry = expectedIter.next(); Iterator<Entry<MarkerTypeIdentityHashMapKey, MarkerTypeIdentityHashMapValue>> mapIter = mapEntries.iterator(); boolean found = false; while (!found && mapIter.hasNext()) { Entry<MarkerTypeIdentityHashMapKey, MarkerTypeIdentityHashMapValue> mapEntry = mapIter.next(); if (mapEntry.getKey().equals(expectedEntry.getKey()) && mapEntry.getValue().equals(expectedEntry.getValue())) { found = true; } } if (!found) { return false; } } return true; } public static boolean isValid(LinkedHashMap<?, ?> expected, LinkedHashMap<?, ?> map) { if (isValid((HashMap<?, ?>) expected, (HashMap<?, ?>) map)) { Iterator<?> expectedEntries = expected.entrySet().iterator(); Iterator<?> actualEntries = map.entrySet().iterator(); return equals(expectedEntries, actualEntries); } return false; } public static boolean isValid(LinkedHashSet<?> expected, LinkedHashSet<?> map) { if (isValid((HashSet<?>) expected, (HashSet<?>) map)) { Iterator<?> expectedEntries = expected.iterator(); Iterator<?> actualEntries = map.iterator(); return equals(expectedEntries, actualEntries); } return false; } public static boolean isValid(LinkedList<?> expected, LinkedList<?> actual) { if (actual == null) { return false; } Iterator<?> expectedEntries = expected.iterator(); Iterator<?> actualEntries = actual.iterator(); return equals(expectedEntries, actualEntries); } public static boolean isValid(SerializablePrivateNoArg actual) { if (actual == null) { return false; } return actual.getValue() == 1; } /** * We want to assert that the two fields have object identity. */ public static boolean isValid(SerializableWithTwoArrays node) { return node.one == node.two; } // also checks whether the sorting of entries is maintained or not. public static boolean isValid(TreeMap<String, MarkerTypeTreeMap> expected, TreeMap<String, MarkerTypeTreeMap> map) { if (map == null) { return false; } if (!equalsWithNullCheck(map.comparator(), expected.comparator())) { return false; } int size = 0; if ((size = expected.size()) != map.size()) { return false; } // entrySet returns entries in the sorted order List<Map.Entry<String, MarkerTypeTreeMap>> actualList = new ArrayList<Map.Entry<String, MarkerTypeTreeMap>>( map.entrySet()); List<Map.Entry<String, MarkerTypeTreeMap>> expectedList = new ArrayList<Map.Entry<String, MarkerTypeTreeMap>>( expected.entrySet()); for (int index = 0; index < size; index++) { Entry<String, MarkerTypeTreeMap> expectedEntry = expectedList.get(index); Entry<String, MarkerTypeTreeMap> actualEntry = actualList.get(index); if (!equalsWithNullCheck(expectedEntry.getKey(), actualEntry.getKey()) || !equalsWithNullCheck(expectedEntry.getValue(), actualEntry.getValue())) { return false; } } return true; } // also checks whether the sorting of entries is maintained or not. public static boolean isValid(TreeSet<MarkerTypeTreeSet> expected, TreeSet<MarkerTypeTreeSet> set) { if (set == null) { return false; } if (!equalsWithNullCheck(set.comparator(), expected.comparator())) { return false; } int size = 0; if ((size = expected.size()) != set.size()) { return false; } // entrySet returns entries in the sorted order List<MarkerTypeTreeSet> actualList = new ArrayList<MarkerTypeTreeSet>(set); List<MarkerTypeTreeSet> expectedList = new ArrayList<MarkerTypeTreeSet>( expected); for (int index = 0; index < size; index++) { if (!equalsWithNullCheck(expectedList.get(index), actualList.get(index))) { return false; } } return true; } public static boolean isValid(UmbrellaException expected, UmbrellaException actual) { if (actual == null) { return false; } /* * Throwable doesn't declare equals. */ if (!equals(expected, actual)) { return false; } /* Check causes. */ Set<Throwable> expectedCauses = expected.getCauses(); Set<Throwable> actualCauses = actual.getCauses(); /* Size. */ if (actualCauses.size() != expectedCauses.size()) { return false; } /* Null elements, and make a copy of the actualCauses set */ Iterator<Throwable> expectedIter = expectedCauses.iterator(); while (expectedIter.hasNext()) { Throwable expectedCause = expectedIter.next(); if (expectedCause == null) { return false; } } Iterator<Throwable> actualIter = actualCauses.iterator(); while (actualIter.hasNext()) { Throwable actualCause = actualIter.next(); if (actualCause == null) { return false; } } /* * The elements themselves. We rely on the fact that the test sets do not * contain duplicates of causes. */ expectedIter = expectedCauses.iterator(); while (expectedIter.hasNext()) { Throwable expectedCause = expectedIter.next(); actualIter = actualCauses.iterator(); boolean found = false; while (!found && actualIter.hasNext()) { Throwable actualCause = actualIter.next(); if (equals(expectedCause, actualCause)) { found = true; } } if (!found) { return false; } } return true; } public static boolean isValid(Vector<?> expected, Vector<?> actual) { if (actual == null) { return false; } return expected.equals(actual); } public static boolean isValidAcyclicGraph(SerializableDoublyLinkedNode actual) { if (actual == null) { return false; } if (!actual.getData().equals("head")) { return false; } SerializableDoublyLinkedNode leftChild = actual.getLeftChild(); if (leftChild == null) { return false; } if (!leftChild.getData().equals("lchild")) { return false; } if (leftChild.getLeftChild() != null || leftChild.getRightChild() != null) { return false; } SerializableDoublyLinkedNode rightChild = actual.getRightChild(); if (rightChild == null) { return false; } if (!rightChild.getData().equals("rchild")) { return false; } if (rightChild.getLeftChild() != null || rightChild.getRightChild() != null) { return false; } return true; } public static boolean isValidArrayListVoid(ArrayList<Void> list) { if (list.size() != 2) { return false; } if (list.get(0) != null || list.get(1) != null) { return false; } return true; } public static boolean isValidAsList(List<?> list) { if (list == null) { return false; } List<?> reference = TestSetFactory.createArraysAsList(); if (reference.size() != list.size()) { return false; } return reference.equals(list); } public static boolean isValidComplexCyclicGraph( SerializableDoublyLinkedNode actual) { assertNotNull(actual); if (actual == null) { return false; } int i = 0; SerializableDoublyLinkedNode currNode = actual; for (; i < 5; ++i) { assertEquals("n" + Integer.toString(i), currNode.getData()); if (!currNode.getData().equals("n" + Integer.toString(i))) { return false; } SerializableDoublyLinkedNode nextNode = currNode.getRightChild(); SerializableDoublyLinkedNode prevNode = currNode.getLeftChild(); assertNotNull("next node", nextNode); assertNotNull("prev node", prevNode); if (nextNode == null || prevNode == null) { return false; } assertSame("A", currNode, nextNode.getLeftChild()); if (nextNode.getLeftChild() != currNode) { return false; } assertSame("B", currNode, prevNode.getRightChild()); if (prevNode.getRightChild() != currNode) { return false; } currNode = currNode.getRightChild(); if (currNode == actual) { break; } } assertFalse("i = " + i, i >= 4); if (i >= 4) { return false; } return true; } public static boolean isValidComplexCyclicGraphWithCFS( SerializableGraphWithCFS result) { assertNotNull(result); List<SerializableGraphWithCFS> array = result.getArray(); assertNotNull(array); assertEquals(1, array.size()); SerializableGraphWithCFS child = array.get(0); assertFalse(result == child); assertSame(result, child.getParent()); return true; } public static boolean isValidRecurisveClassObject(ResultNode<? extends ResultNode<?>> result) { return (result != null); } public static boolean isValidSingletonList(List<MarkerTypeSingleton> list) { if (list == null || list.size() != 1) { return false; } Object value = list.get(0); // Perform instanceof check in case RPC did the wrong thing if (!(value instanceof MarkerTypeSingleton)) { return false; } MarkerTypeSingleton singleton = (MarkerTypeSingleton) value; if (!"singleton".equals(singleton.getValue())) { return false; } return true; } public static boolean isValidTrivialCyclicGraph( SerializableDoublyLinkedNode actual) { if (actual == null) { return false; } if (!actual.getData().equals("head")) { return false; } SerializableDoublyLinkedNode lchild = actual.getLeftChild(); if (lchild == null) { return false; } SerializableDoublyLinkedNode rchild = actual.getRightChild(); if (rchild == null) { return false; } if (actual != lchild && actual != rchild) { return false; } return true; } /** * Wrap an exception in RuntimeException if necessary so it doesn't have to be * listed in throws clauses. * * @param caught exception to wrap */ public static void rethrowException(Throwable caught) { if (caught instanceof RuntimeException) { throw (RuntimeException) caught; } else { throw new RuntimeException(caught); } } private static boolean equalsWithNullCheck(Object a, Object b) { return a == b || (a != null && a.equals(b)); } }