Package org.mule.tck.junit4

Source Code of org.mule.tck.junit4.AbstractMuleTestCase

/*
* Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.com
* The software in this package is published under the terms of the CPAL v1.0
* license, a copy of which has been included with this distribution in the
* LICENSE.txt file.
*/
package org.mule.tck.junit4;

import static org.junit.Assume.assumeThat;
import org.mule.RequestContext;
import org.mule.tck.junit4.rule.WarningTimeout;
import org.mule.util.ClassUtils;
import org.mule.util.IOUtils;
import org.mule.util.MuleUrlStreamHandlerFactory;
import org.mule.util.StringMessageUtils;
import org.mule.util.StringUtils;
import org.mule.util.SystemUtils;

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.commons.collections.IteratorUtils;
import org.apache.commons.collections.Predicate;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.rules.TestName;
import org.junit.rules.TestRule;
import org.junit.rules.Timeout;

/**
* <code>AbstractMuleTestCase</code> is a base class for Mule test cases. This
* implementation provides services to test code for creating mock and test
* objects.
*/
public abstract class AbstractMuleTestCase
{
    public static final String TESTING_MODE_PROPERTY_NAME = "mule.testingMode";

    public static final int DEFAULT_TEST_TIMEOUT_SECS = 60;

    public static final String TEST_TIMEOUT_SYSTEM_PROPERTY = "mule.test.timeoutSecs";

    /**
     * Indicates whether the text boxes will be logged when starting each test case.
     */
    private static final boolean verbose;

    /**
     * Indicates if the current test class was excluded using the mule test
     * exclusion files. Test are executed sequentially, so is not required to
     * maintain a list of classes.
     */
    private static Boolean excluded = null;

    static
    {
        String muleOpts = SystemUtils.getenv("MULE_TEST_OPTS");
        if (StringUtils.isNotBlank(muleOpts))
        {
            Map<String, String> parsedOpts = SystemUtils.parsePropertyDefinitions(muleOpts);
            String optVerbose = parsedOpts.get("mule.verbose");
            verbose = Boolean.valueOf(optVerbose);
        }
        else
        {
            verbose = true;
        }

        System.setProperty(TESTING_MODE_PROPERTY_NAME, StringUtils.EMPTY);

        // register the custom UrlStreamHandlerFactory.
        MuleUrlStreamHandlerFactory.installUrlStreamHandlerFactory();
    }

    protected final transient Log logger = LogFactory.getLog(this.getClass());

    /**
     * Should be set to a string message describing any prerequisites not met.
     */
    private boolean offline = "true".equalsIgnoreCase(System.getProperty("org.mule.offline"));

    private int testTimeoutSecs = getTimeoutSystemProperty();

    @Rule
    public TestName name = new TestName();

    @Rule
    public TestRule globalTimeout = createTestTimeoutRule();

    public AbstractMuleTestCase()
    {
        if (excluded == null)
        {
            excluded = isTestIncludedInExclusionFile(this);
        }
    }

    /**
     * Creates the timeout rule that will be used to run the test.
     *
     * @return the rule used to check for test execution timeouts.
     */
    protected TestRule createTestTimeoutRule()
    {
        int millisecondsTimeout = getTestTimeoutSecs() * 1000;

        if (isFailOnTimeout())
        {
            return new Timeout(millisecondsTimeout);
        }
        else
        {
            return new WarningTimeout(millisecondsTimeout);
        }
    }

    /**
     * Reads the mule-exclusion file for the current test class and
     *
     * @param test
     */
    protected boolean isTestIncludedInExclusionFile(AbstractMuleTestCase test)
    {
        boolean result = false;

        final String testName = test.getClass().getName();
        try
        {
            // We find the physical classpath root URL of the test class and
            // use that to find the correct resource. Works fine everywhere,
            // regardless of classloaders. See MULE-2414
            URL classUrl = ClassUtils.getClassPathRoot(test.getClass());
            URLClassLoader tempClassLoader = new URLClassLoader(new URL[] {classUrl});
            URL fileUrl = tempClassLoader.getResource("mule-test-exclusions.txt");
            if (fileUrl != null)
            {
                InputStream in = null;
                try
                {
                    in = fileUrl.openStream();

                    // this iterates over all lines in the exclusion file
                    Iterator<?> lines = IOUtils.lineIterator(in, "UTF-8");

                    // ..and this finds non-comments that match the test case name
                    result = IteratorUtils.filteredIterator(lines, new Predicate()
                    {
                        @Override
                        public boolean evaluate(Object object)
                        {
                            return StringUtils.equals(testName, StringUtils.trimToEmpty((String) object));
                        }
                    }).hasNext();
                }
                finally
                {
                    IOUtils.closeQuietly(in);
                }
            }
        }
        catch (IOException ioex)
        {
            // ignore
        }

        return result;
    }

    /**
     * Defines the number of seconds that a test has in order to run before
     * throwing a timeout. If the property if not defined then uses the
     * <code>DEFAULT_MULE_TEST_TIMEOUT_SECS</code> constant.
     *
     * @return the timeout value expressed in seconds
     */
    protected int getTimeoutSystemProperty()
    {
        String timeoutString = System.getProperty(TEST_TIMEOUT_SYSTEM_PROPERTY, null);
        if (timeoutString == null)
        {
            // unix style: MULE_TEST_TIMEOUTSECS
            String variableName = TEST_TIMEOUT_SYSTEM_PROPERTY.toUpperCase().replace(".", "_");
            timeoutString = System.getenv(variableName);
        }

        int result = DEFAULT_TEST_TIMEOUT_SECS;
        if (timeoutString != null)
        {
            try
            {
                result = Integer.parseInt(timeoutString);
            }
            catch (NumberFormatException e)
            {
                // Uses the default value
            }
        }

        return result;
    }

    /**
     * Subclasses can override this method to skip the execution of the entire test class.
     *
     * @return <code>true</code> if the test class should not be run.
     */
    protected boolean isDisabledInThisEnvironment()
    {
        return false;
    }

    /**
     * Indicates whether this test has been explicitly disabled through the configuration
     * file loaded by TestInfo.
     *
     * @return whether the test has been explicitly disabled
     */
    protected boolean isExcluded()
    {
        return excluded;
    }

    /**
     * Should this test run?
     *
     * @param testMethodName name of the test method
     * @return whether the test should execute in the current environment
     */
    protected boolean isDisabledInThisEnvironment(String testMethodName)
    {
        return false;
    }

    public boolean isOffline(String method)
    {
        if (offline)
        {
            logger.warn(StringMessageUtils.getBoilerPlate(
                    "Working offline cannot run test: " + method, '=', 80));
        }

        return offline;
    }

    /**
     * Defines the timeout in seconds that will be used to run the test.
     *
     * @return the timeout in seconds
     */
    public int getTestTimeoutSecs()
    {
        return testTimeoutSecs;
    }

    @Before
    public final void initializeMuleTest()
    {
        skipTestWhenExcluded();
        skipTestWhenDisabledInCurrentEnvironment();
        printTestHeader();
    }

    private void printTestHeader()
    {
        if (verbose)
        {
            System.out.println(StringMessageUtils.getBoilerPlate(getTestHeader(), '=', 80));
        }
    }

    protected String getTestHeader()
    {
        return "Testing: " + name.getMethodName();
    }

    private void skipTestWhenExcluded()
    {
        assumeThat(this, new BaseMatcher<AbstractMuleTestCase>()
        {
            @Override
            public boolean matches(Object o)
            {
                return !isExcluded();
            }

            @Override
            public void describeTo(Description description)
            {
                description.appendText("Test " + name.getMethodName() + " is excluded");
            }
        });
    }

    private void skipTestWhenDisabledInCurrentEnvironment()
    {
        assumeThat(this, new BaseMatcher<AbstractMuleTestCase>()
        {
            @Override
            public boolean matches(Object o)
            {
                return !(isDisabledInThisEnvironment() || isDisabledInThisEnvironment(name.getMethodName()));
            }

            @Override
            public void describeTo(Description description)
            {
                description.appendText("Test " + name.getMethodName() + " disabled in this environment");
            }
        });
    }

    /**
     * Indicates whether the test should fail when a timeout is reached.
     * <p/>
     * This feature was added to support old test cases that depend on 3rd-party
     * resources such as a public web service. In such cases it may be desirable
     * to not fail the test upon timeout but rather to simply log a warning.
     *
     * @return true if it must fail on timeout and false otherwise. Default value
     *         is true.
     */
    protected boolean isFailOnTimeout()
    {
        return true;
    }

    @After
    public final void clearRequestContext()
    {
        RequestContext.clear();
    }

    @AfterClass
    public static final void clearExcludedFlag()
    {
        excluded = null;
    }

    private static List<String> collectThreadNames()
    {
        List<String> threadNames = new ArrayList<String>();
        for (Thread t : Thread.getAllStackTraces().keySet())
        {
            if (t.isAlive())
            {
                threadNames.add(t.getName() + " - " + t.getId());
            }
        }
        Collections.sort(threadNames);
        return threadNames;
    }


    private static String testCaseName;

    @BeforeClass
    public static void clearTestCaseName()
    {
        testCaseName = null;
    }

    @Before
    public void takeTestCaseName()
    {
        if (testCaseName == null)
        {
            testCaseName = this.getClass().getName();
        }
    }

    @AfterClass
    public static void dumpFilteredThreadsInTest()
    {
        List<String> currentThreads = collectThreadNames();
        int filteredThreads = 0;
        StringBuilder builder = new StringBuilder();
        for (String threadName : currentThreads)
        {
            if (!nameIn(threadName, "Finalizer", "Monitor Ctrl-Break", "Reference Handler", "Signal Dispatcher", "main"))
            {
                builder.append("\n-> ").append(threadName);
                filteredThreads++;
            }
        }
        if (filteredThreads > 0)
        {
            logThreadsResult(String.format("Hung threads count: %d. Test case: %s. Thread names:%s", filteredThreads, testCaseName, builder.toString()));
        }
        else
        {
            logThreadsResult(String.format("No hung threads. Test case: %s", testCaseName));
        }
    }

    private static boolean nameIn(String threadName, String... values)
    {
        String threadNameLowercase = threadName.toLowerCase();
        if (values != null)
        {
            for (String value : values)
            {
                if (threadNameLowercase.startsWith(value.toLowerCase()))
                {
                    return true;
                }
            }
        }
        return false;
    }

    private static final transient String THREAD_RESULT_LINE = StringUtils.repeat('-', 80);
    private static final transient Log LOGGER = LogFactory.getLog(AbstractMuleTestCase.class);

    private static void logThreadsResult(String result)
    {
        LOGGER.warn(String.format("\n%s\n%s\n%s\n", THREAD_RESULT_LINE, result, THREAD_RESULT_LINE));
    }

}
TOP

Related Classes of org.mule.tck.junit4.AbstractMuleTestCase

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.