001/*
002 * $Id: ComparableTester.java,v 1.11 2017/11/09 20:34:50 oboehm Exp $
003 *
004 * Copyright (c) 2011 by Oliver Boehm
005 *
006 * Licensed under the Apache License, Version 2.0 (the "License");
007 * you may not use this file except in compliance with the License.
008 * You may obtain a copy of the License at
009 *
010 *   http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express orimplied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 *
018 * (c)reated 21.09.2011 by oliver (ob@oasd.de)
019 */
020
021package patterntesting.runtime.junit;
022
023import org.apache.logging.log4j.LogManager;
024import org.apache.logging.log4j.Logger;
025import org.junit.jupiter.api.Assertions;
026import clazzfish.monitor.ClasspathMonitor;
027import patterntesting.runtime.util.Converter;
028
029import java.util.Arrays;
030import java.util.Collection;
031import java.util.List;
032import java.util.regex.Pattern;
033
034/**
035 * This utility class checks classes which implements the {@link Comparable}
036 * interface. E.g. for two objects which are equals it is expected that the
037 * {@link Comparable#compareTo(Object)} method returns 0.
038 *
039 * @author oliver (ob@aosd.de)
040 * @since 1.2 (21.09.2011)
041 */
042public final class ComparableTester extends AbstractTester {
043
044        private static final Logger LOG = LogManager.getLogger(ComparableTester.class);
045        private static final ClasspathMonitor classpathMonitor = ClasspathMonitor.getInstance();
046
047        /** Utility class - no need to instantiate it. */
048        private ComparableTester() {
049        }
050
051        /**
052         * The {@link Comparable#compareTo(Object)} method should return 0 if the
053         * given objects are eqals. If they are not equals the shouldn't return 0.
054         * This is checked here.
055         *
056         * @param c1
057         *            the first Comparable
058         * @param c2
059         *            the second Comparable
060         */
061        @SuppressWarnings({ "rawtypes", "unchecked" })
062        public static void assertCompareTo(final Comparable c1, final Comparable c2) {
063                int ret1 = c1.compareTo(c2);
064                int ret2 = c2.compareTo(c1);
065                if (c1.equals(c2)) {
066                        String msg = c1.getClass() + ": compareTo(..) should return 0 for equals objects";
067                        Assertions.assertEquals(0, ret1, msg);
068                        Assertions.assertEquals(0, ret2, msg);
069                } else {
070                        String msg = c1.getClass() + ": compareTo(..) should return not 0 for not equals objects " + c1 + " and "
071                                        + c2;
072                        Assertions.assertTrue(ret1 != 0, msg);
073                        Assertions.assertTrue(ret2 != 0, msg);
074                        msg = c1.getClass() + ": <" + c2 + ">.compareTo(<" + c1 + ">) should return " + (-ret2) + " (not " + ret2
075                                        + ")";
076                        if (ret1 < 0) {
077                                Assertions.assertTrue(ret2 > 0, msg);
078                        } else {
079                                Assertions.assertTrue(ret2 < 0, msg);
080                        }
081                }
082                LOG.info("compareTo implementation of " + c1.getClass() + " seems to be ok");
083        }
084
085        /**
086         * This method will create two objects of the given class using the default
087         * constructor and compares them. So two preconditions must be true:
088         * <ol>
089         * <li>the class must not be abstract</li>
090         * <li>there must be a (public) default constructor</li>
091         * </ol>
092         *
093         * @param clazz
094         *            the clazz
095         * @throws AssertionError
096         *             if the check fails
097         */
098        @SuppressWarnings("rawtypes")
099        public static void assertCompareTo(final Class<? extends Comparable> clazz) throws AssertionError {
100                LOG.trace("checking {}.compareTo(..)...", clazz);
101                Comparable<?> comp = (Comparable<?>) ObjectTester.newInstanceOf(clazz);
102                Comparable<?> clone = (Comparable<?>) ObjectTester.clone(comp);
103                assertCompareTo(comp, clone);
104        }
105
106        /**
107         * Check for each class in the given collection if the compareTo method
108         * works as expected.
109         *
110         * @param classes
111         *            a collection of classes to be checked
112         */
113        @SuppressWarnings("rawtypes")
114        public static void assertCompareTo(final Collection<Class<? extends Comparable>> classes) {
115                for (Class<? extends Comparable> clazz : classes) {
116                        assertCompareTo(clazz);
117                }
118        }
119
120        /**
121         * Check for each {@link Comparable} class in the given package if the
122         * compareTo(..) method works as expected.
123         * <p>
124         * To get a name of a package call {@link Package#getPackage(String)}. But
125         * be sure that you can't get <em>null</em> as result. In this case use
126         * {@link #assertCompareToOfPackage(String)}.
127         * </p>
128         *
129         * @param pkg
130         *            the package e.g. "patterntesting.runtime"
131         * @see #assertCompareToOfPackage(String)
132         */
133        public static void assertCompareTo(final Package pkg) {
134                assert pkg != null;
135                assertCompareToOfPackage(pkg.getName());
136        }
137
138        /**
139         * Check for each {@link Comparable} class in the given package if the
140         * compareTo(..) method works as expected.
141         * <p>
142         * To get a name of a package call {@link Package#getPackage(String)}. But
143         * be sure that you can't get <em>null</em> as result. In this case use
144         * {@link #assertCompareToOfPackage(String)}.
145         * </p>
146         *
147         * @param pkg
148         *            the package e.g. "patterntesting.runtime"
149         * @param excluded
150         *            class pattern which should be excluded from the check
151         * @see #assertCompareToOfPackage(String, Pattern...)
152         * @since 1.6
153         */
154        public static void assertCompareTo(final Package pkg, final Pattern... excluded) {
155                assert pkg != null;
156                assertCompareToOfPackage(pkg.getName(), excluded);
157        }
158
159        /**
160         * Check for each {@link Comparable} class in the given package if the
161         * compareTo(..) method works as expected.
162         *
163         * @param packageName
164         *            the package name e.g. "patterntesting.runtime"
165         */
166        @SuppressWarnings("rawtypes")
167        public static void assertCompareToOfPackage(final String packageName) {
168                assert packageName != null;
169                Collection<Class<? extends Comparable>> comparables = getComparableClasses(packageName);
170                assertCompareTo(comparables);
171        }
172
173        /**
174     * Check for each {@link Comparable} class in the given package if the
175     * compareTo(..) method works as expected.
176     *
177     * @param packageName the package name e.g. "patterntesting.runtime"
178     * @param excluded classes which should be excluded from the check
179     * @see #assertCompareToOfPackage(String)
180     */
181        @SuppressWarnings("rawtypes")
182    @SafeVarargs
183    public static void assertCompareToOfPackage(final String packageName,
184                        final Class<? extends Comparable<?>>... excluded) {
185        Collection<Class<? extends Comparable>> classes = getComparableClasses(packageName);
186        List<Class<? extends Comparable<?>>> excludedList = Arrays.asList(excluded);
187        LOG.debug("{} will be excluded from check.", excludedList);
188        removeClasses(classes, excludedList);
189        assertCompareTo(classes);
190        }
191
192        /**
193         * Check for each {@link Comparable} class in the given package if the
194         * compareTo(..) method works as expected.
195         *
196         * @param packageName
197         *            the package name e.g. "patterntesting.runtime"
198         * @param excluded
199         *            classes which should be excluded from the check
200         * @see #assertCompareToOfPackage(String)
201         * @since 1.6
202         */
203        @SuppressWarnings("rawtypes")
204        public static void assertCompareToOfPackage(final String packageName, final Pattern... excluded) {
205                Collection<Class<? extends Comparable>> classes = getComparableClasses(packageName);
206                if (LOG.isDebugEnabled()) {
207                        LOG.debug("Pattern {} will be excluded from check.", Converter.toShortString(excluded));
208                }
209                removeClasses(classes, excluded);
210                assertCompareTo(classes);
211        }
212
213        @SuppressWarnings("rawtypes")
214        private static Collection<Class<? extends Comparable>> getComparableClasses(final String packageName) {
215                Collection<Class<? extends Comparable>> comparables = classpathMonitor.getClassList(packageName,
216                                Comparable.class);
217                return comparables;
218        }
219
220}