com.feedzai.fos.server.remote.impl.RemoteInterfacesTest.java Source code

Java tutorial

Introduction

Here is the source code for com.feedzai.fos.server.remote.impl.RemoteInterfacesTest.java

Source

/*
 * $#
 * FOS Server
 * 
 * Copyright (C) 2013 Feedzai SA
 * 
 * This software is licensed under the Apache License, Version 2.0 (the "Apache License") or the GNU
 * Lesser General Public License version 3 (the "GPL License"). You may choose either license to govern
 * your use of this software only upon the condition that you accept all of the terms of either the Apache
 * License or the LGPL License.
 * 
 * You may obtain a copy of the Apache License and the LGPL License at:
 * 
 * http://www.apache.org/licenses/LICENSE-2.0.txt
 * http://www.gnu.org/licenses/lgpl-3.0.txt
 * 
 * Unless required by applicable law or agreed to in writing, software distributed under the Apache License
 * or the LGPL License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 * either express or implied. See the Apache License and the LGPL License for the specific language governing
 * permissions and limitations under the Apache License and the LGPL License.
 * #$
 */
package com.feedzai.fos.server.remote.impl;

import com.feedzai.fos.server.remote.api.RemoteInterface;
import org.apache.commons.collections15.comparators.ComparatorChain;
import org.junit.Test;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.URL;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.util.*;

import static org.junit.Assert.*;

/**
 * This test is a little unorthodox. It tests that Remote exception match with their targeted implementation.
 * It does so by checking {@link com.feedzai.fos.server.remote.api.RemoteInterface} .
 *
 * @author diogo.ferreira
 */
public class RemoteInterfacesTest {
    public RemoteInterfacesTest() {

    }

    @Test
    public void testRemoteInterfaces() throws Exception {
        for (Class klass : getClasses("com.feedzai.fos.server.remote.impl")) {
            if (klass.isAnnotationPresent(RemoteInterface.class)) {
                RemoteInterface implementation = (RemoteInterface) klass.getAnnotation(RemoteInterface.class);
                Class<?> matchingClass = implementation.of();
                testMatch(matchingClass, klass, 0);
            }
        }
    }

    private void testMatch(Class<?> api, Class<?> remote, int remoteFieldAdditions) {
        Method[] apiMethods = api.getMethods();
        Method[] remoteMethods = remote.getMethods();

        Arrays.sort(apiMethods, buildComparator());
        Arrays.sort(remoteMethods, buildComparator());

        // Checks if @RemoteInterface is different from the class/interface itself.
        assertNotSame(
                String.format("@RemoteInterface cannot be the same for the class name in %s", api.getSimpleName()),
                api, remote);
        // Checks if the remote implementation can be assigned to Remote (i.e. if in the hierarchy extends/implements Remote).
        assertTrue(
                String.format("'%s' does not implement '%s'", remote.getSimpleName(), Remote.class.getSimpleName()),
                Remote.class.isAssignableFrom(remote));

        assertEquals(desc(api, null, "Number of methods matches"), apiMethods.length, remoteMethods.length);

        for (int i = 0; i < apiMethods.length; i++) {
            // Check if the names match
            Method expected = apiMethods[i];
            Method actual = remoteMethods[i];

            assertEquals(desc(api, actual, "Names match"), expected.getName(), actual.getName());
            assertTrue(desc(api, actual, "Number of arguments matches"), expected.getParameterTypes().length
                    - actual.getParameterAnnotations().length <= remoteFieldAdditions);

            boolean remoteOrException = false;
            for (Class klass : actual.getExceptionTypes()) {
                remoteOrException = Exception.class.equals(klass) || RemoteException.class.equals(klass);
                if (remoteOrException) {
                    break;
                }
            }

            // Checks if remote implementations throw Exception or RemoteException.
            assertTrue(desc(remote, actual,
                    String.format("%s does not throw either RemoteException or Exception", actual.getName())),
                    remoteOrException);
        }
    }

    private String desc(Class klass, Method method, String desc) {
        String methodName = method == null ? "none" : method.getName();

        return String.format("[%s#%s] %s", klass.getSimpleName(), methodName, desc);
    }

    private Comparator<Method> buildComparator() {
        ComparatorChain<Method> chain = new ComparatorChain<Method>();
        chain.addComparator(new Comparator<Method>() {
            @Override
            public int compare(Method o1, Method o2) {
                return o1.getName().compareTo(o2.getName());
            }
        });
        chain.addComparator(new Comparator<Method>() {
            @Override
            public int compare(Method o1, Method o2) {
                return o1.getParameterTypes().length - o2.getParameterTypes().length;
            }
        });

        return chain;
    }

    /**
     * What follows is a shameless adaptation of http://www.dzone.com/snippets/get-all-classes-within-package
     */

    /**
     * Scans all classes accessible from the context class loader which belong to the given package and subpackages.
     *
     * @param packageName The base package
     * @return The classes
     * @throws ClassNotFoundException
     * @throws java.io.IOException
     */
    private static Class[] getClasses(String packageName) throws ClassNotFoundException, IOException {
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        assert classLoader != null;
        String path = packageName.replace('.', '/');
        Enumeration<URL> resources = classLoader.getResources(path);
        List<File> dirs = new ArrayList<>();
        while (resources.hasMoreElements()) {
            URL resource = resources.nextElement();
            dirs.add(new File(resource.getFile()));
        }
        ArrayList<Class> classes = new ArrayList<>();
        for (File directory : dirs) {
            classes.addAll(findClasses(directory, packageName));
        }
        return classes.toArray(new Class[classes.size()]);
    }

    /**
     * Recursive method used to find all classes in a given directory and subdirs.
     *
     * @param directory   The base directory
     * @param packageName The package name for classes found inside the base directory
     * @return The classes
     * @throws ClassNotFoundException
     */
    private static List<Class> findClasses(File directory, String packageName) throws ClassNotFoundException {
        List<Class> classes = new ArrayList<>();
        if (!directory.exists()) {
            return classes;
        }
        File[] files = directory.listFiles();
        for (File file : files) {
            if (file.isDirectory()) {
                assert !file.getName().contains("");
                classes.addAll(findClasses(file, packageName + "" + file.getName()));
            } else if (file.getName().contains("Remote")) {
                try {
                    classes.add(Class
                            .forName(packageName + '.' + file.getName().substring(0, file.getName().length() - 6)));
                } catch (NullPointerException ex) {
                    // Some classes may fail their static initialization, we don't really care.
                }
            }
        }
        return classes;
    }
}