All Downloads are FREE. Search and download functionalities are using the official Maven repository.

name.didier.david.test4j.unit.TestResourceHandler Maven / Gradle / Ivy

The newest version!
package name.didier.david.test4j.unit;

import static java.lang.String.format;
import static java.nio.charset.StandardCharsets.UTF_8;

import static name.didier.david.check4j.ConciseCheckers.checkNotNull;

import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.List;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.io.Resources;

/**
 * Handle {@link TestResource}s and their associated resources.
 *
 * @author ddidier
 */
public class TestResourceHandler {

    /** The supported types of resources that can be injected. */
    private static final Class[] SUPPORTED_TYPES = new Class[] { URL.class, File.class, String.class,
        List.class, InputStream.class, InputStreamReader.class, };

    /** The associated logger. */
    private static final Logger logger = LoggerFactory.getLogger(TestResourceHandler.class);

    /** The naming strategy. */
    private final TestResourceNamingStrategy namingStrategy;
    /** The resolution strategy. */
    private final TestResourceResolutionStrategy resolutionStrategy;

    /**
     * Default constructor using a {@link DefaultTestResourceNamingStrategy} and a
     * {@link DefaultTestResourceResolutionStrategy} strategies.
     */
    public TestResourceHandler() {
        this(new DefaultTestResourceNamingStrategy(), new DefaultTestResourceResolutionStrategy());
    }

    /**
     * Default constructor.
     *
     * @param namingStrategy the naming strategy.
     * @param resolutionStrategy the resolution strategy.
     */
    public TestResourceHandler(
            final TestResourceNamingStrategy namingStrategy,
            final TestResourceResolutionStrategy resolutionStrategy) {
        super();
        this.namingStrategy = checkNotNull(namingStrategy, "namingStrategy");
        this.resolutionStrategy = checkNotNull(resolutionStrategy, "resolutionStrategy");
    }

    /**
     * @return the naming strategy.
     */
    public TestResourceNamingStrategy getNamingStrategy() {
        return namingStrategy;
    }

    /**
     * @return the resolution strategy.
     */
    public TestResourceResolutionStrategy getResolutionStrategy() {
        return resolutionStrategy;
    }

    /**
     * Find the resource with the given pattern as a {@link File}.
     *
     * @param pattern the pattern to search for.
     * @param testInstance the instance of the running test.
     * @param testMethod the method of the running test.
     * @return the file if found.
     * @throws TestResourceException if the resource cannot be found.
     */
    public File findFile(final String pattern, final Object testInstance, final Method testMethod) {
        return new File(findUrl(pattern, testInstance, testMethod).getPath());
    }

    /**
     * Find the resource with the given pattern as a {@link URL}.
     *
     * @param pattern the pattern to search for.
     * @param testInstance the instance of the running test.
     * @param testMethod the method of the running test.
     * @return the URL if found.
     * @throws TestResourceException if the resource cannot be found.
     */
    public URL findUrl(final String pattern, final Object testInstance, final Method testMethod) {
        Class testClass = testInstance.getClass();
        String resourceName = namingStrategy.resourceName(pattern, testClass, null, testMethod);
        URL url = resolutionStrategy.findResource(testClass, resourceName);

        if (url == null) {
            String message = format("Resource not found for pattern '%s'", pattern);
            throw new TestResourceException(message, testInstance, testMethod);
        }

        return url;
    }

    /**
     * Load the content of the resource with the given pattern.
     *
     * @param pattern the pattern to search for.
     * @param testInstance the instance of the running test.
     * @param testMethod the method of the running test.
     * @return the content of the resource if found.
     * @throws TestResourceException if the resource cannot be found.
     */
    public String loadContent(final String pattern, final Object testInstance, final Method testMethod) {
        return read(findUrl(pattern, testInstance, testMethod), testInstance, testMethod);
    }

    /**
     * Interpolate the pattern as a relative path. See {@link TestResource} and
     * {@link TestResourceNamingStrategy#resourceName(String, Class, Field, Method)}.
     *
     * @param pattern the pattern to interpolate.
     * @param testInstance the instance of the running test.
     * @param testMethod the method of the running test.
     * @return the interpolated pattern as a relative path.
     */
    public String toPath(final String pattern, final Object testInstance, final Method testMethod) {
        Class testClass = testInstance.getClass();
        return namingStrategy.resourceName(pattern, testClass, null, testMethod);
    }

    /**
     * Inject all the {@link TestResource}s of the given instance for the given test.
     *
     * @param testInstance the instance of the running test.
     * @param testMethod the method of the running test.
     * @throws TestResourceException if a resource cannot be found or read.
     */
    public void inject(final Object testInstance, final Method testMethod) {
        Class testClass = testInstance.getClass();
        List testFields = FieldUtils.getFieldsListWithAnnotation(testClass, TestResource.class);

        for (Field testField : testFields) {
            logger.trace("Found field annotated with TestResource: {}", testField);

            if (!isInjectionTypeSupported(testField.getType())) {
                String message = format("Unsupported resource type for %s", testField);
                throw new TestResourceException(message, testInstance, testMethod);
            }

            TestResource testResource = testField.getAnnotation(TestResource.class);
            String[] testPatterns = testResource.value();
            List resourceNames = namingStrategy.resourceNames(testPatterns, testClass, testField, testMethod);
            URL url = resolutionStrategy.findResource(testClass, resourceNames);

            if (url == null) {
                if (!testResource.optional()) {
                    throw new RuntimeException("Resource not found for " + testField);
                }
                logger.trace("Resource not found for {}", testField);
            } else {
                logger.trace("Resource found for {}: {}", testField, url);
                setResource(testInstance, testMethod, testField, url);
            }
        }
    }

    /**
     * Clear all the {@link TestResource}s of the given instance for the given test.
     *
     * @param testInstance the instance of the running test.
     * @param testMethod the method of the running test.
     */
    public void clear(final Object testInstance, final Method testMethod) {
        Class testClass = testInstance.getClass();
        List testFields = FieldUtils.getFieldsListWithAnnotation(testClass, TestResource.class);

        for (Field testField : testFields) {
            clearResource(testInstance, testMethod, testField);
        }
    }

    /**
     * Check that the given resource can be injected, that is its type is supported.
     *
     * @param resourceClass the class of the resource to check.
     * @return true if the given class is supported, false otherwise.
     */
    protected boolean isInjectionTypeSupported(Class resourceClass) {
        return ArrayUtils.contains(SUPPORTED_TYPES, resourceClass);
    }

    /**
     * Set the given URL on the field of the given instance.
     *
     * @param testInstance the instance of the running test to set the field on.
     * @param testMethod the method of the running test.
     * @param testField the field to set.
     * @param url the URL to set.
     * @throws TestResourceException if the resource cannot be read.
     */
    protected void setResource(final Object testInstance, final Method testMethod, final Field testField,
            final URL url) {
        try {
            testField.setAccessible(true);
            Class fieldType = testField.getType();

            if (URL.class.isAssignableFrom(fieldType)) {
                testField.set(testInstance, url);
            } else if (File.class.isAssignableFrom(fieldType)) {
                testField.set(testInstance, new File(url.getPath()));
            } else if (String.class.isAssignableFrom(fieldType)) {
                testField.set(testInstance, read(url, null, null));
            } else if (List.class.isAssignableFrom(fieldType)) {
                testField.set(testInstance, readLines(url, testInstance, testMethod));
            } else if (InputStream.class.isAssignableFrom(fieldType)) {
                testField.set(testInstance, open(url, testInstance, testMethod));
            } else if (InputStreamReader.class.isAssignableFrom(fieldType)) {
                testField.set(testInstance, new InputStreamReader(open(url, testInstance, testMethod), UTF_8));
            } else {
                String message = format("Unsupported resource type for %s", testField);
                throw new TestResourceException(message, testInstance, testMethod);
            }
        } catch (IllegalArgumentException | IllegalAccessException e) {
            throw new TestResourceException("Error while setting resource", e, testInstance, testMethod);
        }
    }

    /**
     * Clear the given field of the given instance.
     *
     * @param testInstance the instance of the running test to clear the field on.
     * @param testMethod the method of the running test.
     * @param testField the field to clear.
     * @throws TestResourceException if the resource cannot be cleared.
     */
    protected void clearResource(final Object testInstance, final Method testMethod, final Field testField) {
        try {
            testField.setAccessible(true);
            Class fieldType = testField.getType();

            if (URL.class.isAssignableFrom(fieldType)) {
                testField.set(testInstance, null);
            } else if (File.class.isAssignableFrom(fieldType)) {
                testField.set(testInstance, null);
            } else if (String.class.isAssignableFrom(fieldType)) {
                testField.set(testInstance, null);
            } else if (List.class.isAssignableFrom(fieldType)) {
                testField.set(testInstance, null);
            } else if (InputStream.class.isAssignableFrom(fieldType)) {
                IOUtils.closeQuietly((Closeable) testField.get(testInstance));
                testField.set(testInstance, null);
            } else if (InputStreamReader.class.isAssignableFrom(fieldType)) {
                IOUtils.closeQuietly((Closeable) testField.get(testInstance));
                testField.set(testInstance, null);
            }
            // else {
            // no need
            // String message = format("Unsupported resource type for %s", testField);
            // throw new TestResourceException(message, testInstance, testMethod);
            // }
        } catch (IllegalArgumentException | IllegalAccessException e) {
            throw new TestResourceException("Error while clearing resource", e, testInstance, testMethod);
        }
    }

    /**
     * @param url the resource to read.
     * @param testInstance the instance of the running test.
     * @param testMethod the method of the running test.
     * @return the content of the given resource.
     * @throws TestResourceException if the resource cannot be read.
     */
    private String read(final URL url, final Object testInstance, final Method testMethod) {
        try {
            return Resources.toString(url, UTF_8);
        } catch (IOException e) {
            String message = format("Error while reading URL '%s'", url);
            throw new TestResourceException(message, e, testInstance, testMethod);
        }
    }

    /**
     * @param url the resource to read.
     * @param testInstance the instance of the running test.
     * @param testMethod the method of the running test.
     * @return the content of the given resource.
     * @throws TestResourceException if the resource cannot be read.
     */
    private List readLines(final URL url, final Object testInstance, final Method testMethod) {
        try {
            return Resources.readLines(url, UTF_8);
        } catch (IOException e) {
            String message = format("Error while reading URL '%s'", url);
            throw new TestResourceException(message, e, testInstance, testMethod);
        }
    }

    /**
     * @param url the resource to open.
     * @param testInstance the instance of the running test.
     * @param testMethod the method of the running test.
     * @return a stream of the given resource.
     * @throws TestResourceException if the resource cannot be read.
     */
    private InputStream open(final URL url, final Object testInstance, final Method testMethod) {
        try {
            return url.openStream();
        } catch (IOException e) {
            String message = format("Error while opening stream from URL '%s'", url);
            throw new TestResourceException(message, e, testInstance, testMethod);
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy