/* Copyright (c) 2011 Danish Maritime Authority. * * 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 net.maritimecloud.internal.mms.client; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.NotSerializableException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.ref.Reference; import java.lang.reflect.Array; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Queue; import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.function.Supplier; import junit.framework.AssertionFailedError; import org.junit.Assert; import org.junit.internal.ArrayComparisonFailure; /** * And extension to {@link Assert} with useful asserts. * * @author Kasper Nielsen */ public class MoreAsserts extends Assert { public static void assertThrows(Class<? extends Throwable> expected, String msg, Runnable r) { try { r.run(); } catch (Throwable t) { if (!expected.isAssignableFrom(t.getClass())) { throw new AssertionError("expected " + expected.getSimpleName() + ", but was " + t.getClass().getSimpleName(), t); } assertEquals(msg, t.getMessage()); return; } throw new AssertionError("expected " + expected.getSimpleName()); } @SafeVarargs public static void assertThrows(Class<? extends Throwable> expected, Runnable r, Class<? extends Throwable>... causes) { try { r.run(); } catch (Throwable t) { if (!expected.isAssignableFrom(t.getClass())) { throw new AssertionError("expected " + expected.getSimpleName() + ", but was " + t.getClass().getSimpleName(), t); } assertCausesEquals(t.getCause(), new LinkedList<>(Arrays.asList(causes))); return; } throw new AssertionError("expected " + expected.getSimpleName()); } private static void assertCausesEquals(Throwable cause, Queue<Class<? extends Throwable>> causes) { if (cause == null) { assertTrue(causes.isEmpty()); } else { assertFalse(causes.isEmpty()); Class<? extends Throwable> t = causes.poll(); assertTrue("Expected cause of type " + t + " but was " + cause.getClass(), t.isAssignableFrom(cause.getClass())); assertCausesEquals(cause.getCause(), causes); } } public static void assertNullPointerException(Runnable r) { try { r.run(); throw new AssertionError("Exception NullPointerException"); } catch (NullPointerException ok) {} } public static void assertUtilityClass(Class<?> c) { assertTrue("Class " + c.getCanonicalName() + " should be final", Modifier.isFinal(c.getModifiers())); assertEquals(1, c.getDeclaredConstructors().length); Constructor<?> constructor; try { constructor = c.getDeclaredConstructor(); } catch (NoSuchMethodException e) { throw new AssertionError(e); } assertFalse(constructor.isAccessible()); assertTrue(Modifier.isPrivate(constructor.getModifiers())); // This is mostly for coverage constructor.setAccessible(true); try { constructor.newInstance(); } catch (ReflectiveOperationException e) { throw new AssertionError(e); } constructor.setAccessible(false); for (Method method : c.getDeclaredMethods()) { assertTrue(Modifier.isStatic(method.getModifiers())); } } private static <T> List<T> _asList(Iterable<? extends T> iterable) { Iterator<? extends T> iterator = iterable.iterator(); ArrayList<T> result = new ArrayList<>(); while (iterator.hasNext()) { result.add(iterator.next()); } return result; } public static void assertTrueWithin(Supplier<Boolean> sup, long timeout, TimeUnit unit) throws InterruptedException { if (sup.get()) { return; } long now = System.nanoTime(); long deadline = now + unit.toNanos(timeout); long sleep = 1; while (now < deadline) { sleep = Math.min(sleep * 2, deadline - now); TimeUnit.NANOSECONDS.sleep(sleep); if (sup.get()) { return; } now = System.nanoTime(); } throw new AssertionError("Timed out"); } public static void assertArrayEquals(Object[] expected, Object[] actual) { try { Assert.assertArrayEquals(expected, actual); } catch (ArrayComparisonFailure e) { System.err.println("Expected " + Arrays.toString(expected)); System.err.println("Actual " + Arrays.toString(actual)); throw e; } } public static void assertArrayEquals(Object[] expected, Object[] actuals, boolean isOrdered) { if (isOrdered) { assertArrayEquals(expected, actuals); } else { assertArrayEqualsInAnyOrder(expected, actuals); // // assertEquals( // "Expected another length of the array, [expected length= " + expected.length + ", was length=" // + actuals.length + "] [expected elements=" + Arrays.toString(expected) + ", was=" // + Arrays.toString(actuals) + "]", expected.length, actuals.length); // TODO compare Sets of the two } } static void assertArrayEqualsInAnyOrder(Object[] expected, Object[] actual) { assertArraysAreSameLength(expected, actual); assertEquals(0, assertArrayIsArraySubset0(expected, actual).size()); } public static void assertArrayEqualsInAnyOrder(Object[] expected, Object[] actual, boolean isOrdered) { if (isOrdered) { assertArrayEquals(expected, actual); } else { assertArrayEqualsInAnyOrder(expected, actual); } } static Map<Object, Integer> assertArrayIsArraySubset0(Object[] superSet, Object[] subSet) { Map<Object, Integer> map = new HashMap<>(); for (Object o : superSet) { Integer l = map.get(o); map.put(o, l == null ? 1 : l + 1); } for (Object o : subSet) { Integer l = map.get(o); if (l == null) { fail("Element " + o + " was not expected [actual type=" + o.getClass() + ": expected type = " + superSet[0].getClass()); } else if (l.equals(1)) { map.remove(o); } else { map.put(o, l - 1); } } return map; } public static void assertArrayIsSubsetInAnyOrder(Object[] expected, Object[] actual) { assertTrue(actual.length <= expected.length); assertArrayIsArraySubset0(expected, actual).size(); } private static void assertArraysAreSameLength(Object expected, Object actual) { int actualLength = Array.getLength(actual); int expectedLength = Array.getLength(expected); if (actualLength != expectedLength) { fail("Array lengths differed [expected.length=" + expectedLength + ", actual.length=" + actualLength + "]"); } } public static void assertSetEquals(Set<?> actual, Object... expected) { Set<?> set = new HashSet<>(Arrays.asList(expected));// Eclipse compiler assertEquals(set, actual); } public static void assertCollectionEqualInAnyOrder(Iterable<?> expected, Collection<?> actual) { assertArrayEqualsInAnyOrder(_asList(expected).toArray(), actual.toArray()); } public static void assertCollectionIsCollectionSubset(Iterable<?> expected, Collection<?> actual) { assertArrayIsSubsetInAnyOrder(_asList(expected).toArray(), actual.toArray()); } public static <T> T assertIsSerializable(T o) { T result = null; try { result = readWrite(o); } catch (NotSerializableException e) { throw new AssertionFailedError(o + " not serialiable"); } Class<?> clz = o.getClass(); final Field f; try { f = clz.getDeclaredField("serialVersionUID"); } catch (NoSuchFieldException e) { throw new AssertionError("Clazz " + clz.getCanonicalName() + " does not declare a serialVersionUID field"); } catch (SecurityException e) { throw new Error(e); } if (!Modifier.isStatic(f.getModifiers())) { throw new AssertionError("Clazz " + clz.getCanonicalName() + " does declare a serialVersionUID field, but it is not static"); } assertNotNull(result); return result; } public static void assertIterableEquals(Iterable<?> expected, Iterable<?> actual) { Iterator<?> e = expected.iterator(); Iterator<?> a = actual.iterator(); while (e.hasNext()) { assertTrue(a.hasNext()); assertEquals(e.next(), a.next()); } assertFalse(a.hasNext()); } /** Compares to list ignoring the order of elements in the lists. */ /** * @param i1 * @param i2 */ public static void assertIterableEqualsInAnyOrder(Collection<? extends Comparable<?>> i1, Collection<? extends Comparable<?>> i2) { List<? extends Comparable<?>> l1 = new ArrayList<>(i1); List<? extends Comparable<?>> l2 = new ArrayList<>(i1); Assert.assertEquals(l1.size(), l2.size()); Comparator<Comparable<?>> cmp = new Comparator<Comparable<?>>() { @SuppressWarnings({ "rawtypes", "unchecked" }) public int compare(Comparable o1, Comparable o2) { return o1 == null ? -1 : o2 == null ? 1 : o1.compareTo(o2); } }; Collections.sort(l1, cmp); Collections.sort(l2, cmp); Assert.assertEquals(l1, l2); } public static void assertMapIsMapSubset(Map<?, ?> fullMap, Map<?, ?> submap) { submap.forEach((k, v) -> { assertTrue(fullMap.containsKey(k));// need to check this if value=null assertEquals(fullMap.get(k), v); }); } public static void assertNotSerializable(Object o) { try { readWrite(o); throw new AssertionFailedError(o + " is serialiable"); } catch (NotSerializableException nse) {/* ok */ } } public static <T> T assertOne(Iterable<T> iterable) { Iterator<T> iterator = iterable.iterator(); if (!iterator.hasNext()) { throw new AssertionError("Specified iterable is empty"); } T t = iterator.next(); if (iterator.hasNext()) { throw new AssertionError("Specified iterable contains more than 1 element"); } return t; } public static <T> T assertOne(String message, Iterable<T> iterable) { Iterator<T> iterator = iterable.iterator(); if (iterator.hasNext()) { T t = iterator.next(); if (!iterator.hasNext()) { return t; } } throw new AssertionError(message); } /** * A best effort tet * * @param text * the error text in case the reference was not garbage collected. * @param ref * the reference to test */ public static void assertReferenceIsGCed(String text, Reference<?> ref) { List<byte[]> alloc = new ArrayList<>(); int size = 100000; for (int i = 0; i < 50; i++) { if (ref.get() == null) { return; } try { System.gc(); } catch (OutOfMemoryError ignore) {} try { System.runFinalization(); } catch (OutOfMemoryError ignore) {} try { alloc.add(new byte[size]); size = (int) (size * 1.3d); } catch (OutOfMemoryError ignore) { size = size / 2; } try { if (i % 3 == 0) { Thread.sleep(i * 5); } } catch (InterruptedException ignore) {} } fail(text); } public static void assertSerializableAndEquals(Object expectedAndActual) { assertSerializableAndEquals(expectedAndActual, expectedAndActual); } public static void assertSerializableAndEquals(Object expected, Object actual) { final Object o; try { o = readWrite(actual); } catch (NotSerializableException e) { throw new AssertionFailedError(actual + " not serialiable"); } assertEquals(expected, o); } public static void assertSingletonSerializable(Object o) { assertIsSerializable(o); Assert.assertSame(o, serializeAndUnserialize(o)); } @SuppressWarnings("unchecked") private static <T> T readWrite(T o) throws NotSerializableException { try { ByteArrayOutputStream bout = new ByteArrayOutputStream(20000); try (ObjectOutputStream out = new ObjectOutputStream(new BufferedOutputStream(bout));) { out.writeObject(o); } ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray()); ObjectInputStream in = new ObjectInputStream(new BufferedInputStream(bin)); return (T) in.readObject(); } catch (NotSerializableException nse) { throw nse; } catch (ClassNotFoundException | IOException e) { throw new Error(e);// should'nt happen } } /** * A helper method that serializes and deserializes an object. * * @param o * the object to serialize and deserialize * @return a serialized and deserialized instance of the specified object */ public static <T> T serializeAndUnserialize(T o) { try { return readWrite(o); } catch (Exception e) { throw new AssertionError(o + " not serialiable", e); } } }