001/*- 002 * Copyright (c) 2012, 2016 Diamond Light Source Ltd. 003 * 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the Eclipse Public License v1.0 006 * which accompanies this distribution, and is available at 007 * http://www.eclipse.org/legal/epl-v10.html 008 */ 009 010package org.eclipse.january.asserts; 011 012import java.util.Arrays; 013 014import org.eclipse.january.dataset.BooleanDataset; 015import org.eclipse.january.dataset.Dataset; 016import org.eclipse.january.dataset.IndexIterator; 017import org.eclipse.january.dataset.InterfaceUtils; 018import org.eclipse.january.dataset.ObjectDataset; 019import org.eclipse.january.dataset.StringDataset; 020import org.junit.Assert; 021import org.junit.runner.Description; 022import org.junit.runner.notification.RunListener; 023import org.junit.runner.notification.RunNotifier; 024import org.junit.runners.Suite; 025import org.junit.runners.model.InitializationError; 026import org.junit.runners.model.RunnerBuilder; 027 028public class TestUtils { 029 static RunListener listener; 030 static { 031 listener = new RunListener() { 032 @Override 033 public void testStarted(Description description) throws Exception { 034 if (verbosity != Verbosity.QUIET) { 035 System.out.println("Starting: " + description.getMethodName()); 036 } 037 super.testStarted(description); 038 } 039 040 @Override 041 public void testFinished(Description description) throws Exception { 042 super.testFinished(description); 043 if (verbosity != Verbosity.QUIET) { 044 System.out.println("Finished: " + description.getMethodName()); 045 } 046 } 047 }; 048 } 049 050 /** 051 * Suite that adds a Run listener that prints when methods start and finish 052 */ 053 public static class VerboseSuite extends Suite { 054 055 // TODO handle suites of suites properly 056 public VerboseSuite(Class<?> klass, RunnerBuilder builder) throws InitializationError { 057 super(klass, builder); 058 } 059 060 @Override 061 public void run(RunNotifier notifier) { 062 // To avoid duplicates need to do N-1 times if there's N levels of these suites 063 // Note, in this notifier implementation it does not matter if we try to remove 064 // a listener if is not contained with the notifier 065 notifier.removeListener(listener); 066 067 notifier.addListener(listener); 068 super.run(notifier); 069 notifier.removeListener(listener); 070 } 071 } 072 073 /** 074 * Assert equality of datasets where each item is true if they are equal or 075 * for floating point datasets, if <code>abs(a - b) ≤ 1e-12 + 1e-12*abs(b)</code> 076 * <p> 077 * The dataset types have to match and so does the number of elements in each item. 078 * <p> 079 * Same as <pre>TestUtils.assertDatasetEquals(expected, actual, true, 1e-12, 1e-12)</pre> 080 * @param expected dataset 081 * @param actual dataset 082 */ 083 public static void assertDatasetEquals(Dataset expected, Dataset actual) { 084 assertDatasetEquals(expected, actual, true, 1e-12, 1e-12); 085 } 086 087 /** 088 * Assert equality of datasets where each element is true if they are equal or 089 * for floating point datasets, if <code>abs(a - b) ≤ 1e-12 + 1e-12*abs(b)</code> 090 * @param expected dataset 091 * @param actual dataset 092 * @param testDTypeAndItemSize if true, check dataset type and number of elements 093 */ 094 public static void assertDatasetEquals(Dataset expected, Dataset actual, boolean testDTypeAndItemSize) { 095 assertDatasetEquals(expected, actual, testDTypeAndItemSize, 1e-12, 1e-12); 096 } 097 098 /** 099 * Assert equality of datasets where each element is true if they are equal or 100 * for floating point datasets, if <code>abs(a - b) ≤ absTolerance + relTolerance*abs(b)</code>. 101 * <p> 102 * The dataset types does not have to match nor does the number of elements in each item. 103 * @param expected dataset 104 * @param actual dataset 105 * @param relTolerance relative tolerance 106 * @param absTolerance absolute tolerance 107 */ 108 public static void assertDatasetEquals(Dataset expected, Dataset actual, double relTolerance, double absTolerance) { 109 assertDatasetEquals(expected, actual, false, relTolerance, absTolerance); 110 } 111 112 /** 113 * Assert equality of datasets where each element is true if they are equal or 114 * for floating point datasets, if <code>abs(a - b) ≤ absTolerance + relTolerance*abs(b)</code>. 115 * @param expected dataset 116 * @param actual dataset 117 * @param testDTypeAndItemSize if true, check dataset type and number of elements 118 * @param relTolerance relative tolerance 119 * @param absTolerance absolute tolerance 120 */ 121 public static void assertDatasetEquals(Dataset expected, Dataset actual, boolean testDTypeAndItemSize, double relTolerance, double absTolerance) { 122 final int eis = expected.getElementsPerItem(); 123 final int ais = actual.getElementsPerItem(); 124 Class<? extends Dataset> clazz = InterfaceUtils.findSubInterface(expected.getClass()); 125 if (testDTypeAndItemSize) { 126 Assert.assertEquals("Interface", clazz, InterfaceUtils.findSubInterface(actual.getClass())); 127 Assert.assertEquals("Itemsize", eis, ais); 128 } 129 Assert.assertEquals("Size", expected.getSize(), actual.getSize()); 130 try { 131 Assert.assertArrayEquals("Shape", expected.getShapeRef(), actual.getShapeRef()); 132 } catch (AssertionError e) { 133 if (actual.getSize() == 1) { 134 Assert.assertArrayEquals("Shape", new int[0], actual.getShapeRef()); 135 } else { 136 throw e; 137 } 138 } 139 IndexIterator et = expected.getIterator(true); 140 IndexIterator at = actual.getIterator(); 141 final int is = Math.max(eis, ais); 142 143 int n = 0; 144 if (InterfaceUtils.isInteger(clazz)) { 145 while (et.hasNext() && at.hasNext()) { 146 for (int j = 0; j < is; j++) { 147 long e = j >= eis ? 0 : expected.getElementLongAbs(et.index + j); 148 long a = j >= ais ? 0 : actual.getElementLongAbs(at.index + j); 149 Assert.assertEquals("Value does not match at " + Arrays.toString(et.getPos()) + "; " + j + 150 ": ", e, a); 151 } 152 n++; 153 } 154 } else if (InterfaceUtils.isFloating(clazz)) { 155 while (et.hasNext() && at.hasNext()) { 156 for (int j = 0; j < is; j++) { 157 double e = j >= eis ? 0 : expected.getElementDoubleAbs(et.index + j); 158 double a = j >= ais ? 0 : actual.getElementDoubleAbs(at.index + j); 159 assertEquals("Value does not match at " + Arrays.toString(et.getPos()) + "; " + j + 160 ": ", e, a, relTolerance, absTolerance); 161 } 162 n++; 163 } 164 } else if (BooleanDataset.class.isAssignableFrom(clazz)) { 165 while (et.hasNext() && at.hasNext()) { 166 for (int j = 0; j < is; j++) { 167 boolean e = j >= eis ? false : expected.getElementBooleanAbs(et.index + j); 168 boolean a = j >= ais ? false : actual.getElementBooleanAbs(at.index + j); 169 Assert.assertEquals("Value does not match at " + Arrays.toString(et.getPos()) + "; " + j + 170 ": ", e, a); 171 } 172 n++; 173 } 174 } else if (StringDataset.class.isAssignableFrom(clazz)) { 175 StringDataset es = (StringDataset) expected; 176 StringDataset as = (StringDataset) actual; 177 178 while (et.hasNext() && at.hasNext()) { 179 Assert.assertEquals("Value does not match at " + Arrays.toString(et.getPos()) + ": ", 180 es.getAbs(et.index), as.getAbs(at.index)); 181 n++; 182 } 183 } else if (ObjectDataset.class.isAssignableFrom(clazz)) { 184 ObjectDataset eo = (ObjectDataset) expected; 185 ObjectDataset ao = (ObjectDataset) actual; 186 187 while (et.hasNext() && at.hasNext()) { 188 Assert.assertEquals("Value does not match at " + Arrays.toString(et.getPos()) + ": ", 189 eo.getAbs(et.index), ao.getAbs(at.index)); 190 n++; 191 } 192 } else { 193 while (et.hasNext() && at.hasNext()) { 194 Assert.assertEquals("Value does not match at " + Arrays.toString(et.getPos()) + ": ", 195 expected.getObjectAbs(et.index), actual.getObjectAbs(at.index)); 196 n++; 197 } 198 } 199 200 Assert.assertEquals("Total items checked", expected.getSize(), n); 201 } 202 203 /** 204 * Assert equality if <code>abs(e - a) ≤ max(1e-20, 1e-14*max(abs(e), abs(a)))</code> 205 * @param s message for assert exception 206 * @param e expected value 207 * @param a actual value 208 */ 209 public static void assertEquals(String s, double e, double a) { 210 assertEquals(s, e, a, 1e-14, 1e-20); 211 } 212 213 /** 214 * Assert equality if <code>abs(e - a) ≤ max(absTol, relTol*max(abs(e), abs(a)))</code> 215 * @param s message for assert exception 216 * @param e expected value 217 * @param a actual value 218 * @param relTol relative tolerance 219 * @param absTol absolute tolerance 220 */ 221 public static void assertEquals(String s, double e, double a, double relTol, double absTol) { 222 double t = Math.max(absTol, relTol*Math.max(Math.abs(e), Math.abs(a))); 223 Assert.assertEquals(s, e, a, t); 224 } 225 226 public static enum Verbosity { 227 /** 228 * Completely quiet 229 */ 230 QUIET, 231 /** 232 * Output test method entry and exit 233 */ 234 TEST_METHOD, 235 /** 236 * Output all 237 */ 238 VERBOSE; 239 } 240 241 private static Verbosity verbosity = Verbosity.QUIET; 242 243 /** 244 * @param verbosity level 245 */ 246 public static void setVerbosity(Verbosity verbosity) { 247 TestUtils.verbosity = verbosity; 248 } 249 250 /** 251 * Print when verbosity set to verbose 252 * @param fmt format 253 * @param args arguments 254 */ 255 public static void verbosePrintf(String fmt, Object... args) { 256 if (verbosity == Verbosity.VERBOSE) { 257 System.out.printf(fmt, args); 258 } 259 } 260 261 /** 262 * Print when verbosity set to verbose 263 * @param arg argument 264 */ 265 public static void verbosePrintln(String arg) { 266 if (verbosity == Verbosity.VERBOSE) { 267 System.out.println(arg); 268 } 269 } 270}