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) &le; 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) &le; 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) &le; 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) &le; 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) &le; 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) &le; 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}