org.apache.cactus.internal.client.ClientTestCaseCaller.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.cactus.internal.client.ClientTestCaseCaller.java

Source

/* 
 * ========================================================================
 * 
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 * ========================================================================
 */
package org.apache.cactus.internal.client;

import junit.framework.Assert;
import junit.framework.Test;
import org.apache.cactus.Request;
import org.apache.cactus.internal.util.JUnitVersionHelper;
import org.apache.cactus.internal.util.TestCaseImplementChecker;
import org.apache.cactus.spi.client.ResponseObjectFactory;
import org.apache.cactus.spi.client.connector.ProtocolHandler;
import org.apache.cactus.spi.client.connector.ProtocolState;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * Provides the ability to run common code before and after each test on the 
 * client side. All the methods provided are independent of any communication 
 * protocol between client side and server side (HTTP, JMS, etc). Any protocol 
 * dependent methods must be provided and implemented in the 
 * {@link ProtocolHandler} implementation class.
 *  
 * @version $Id: ClientTestCaseCaller.java 238991 2004-05-22 11:34:50Z vmassol $
 */
public class ClientTestCaseCaller extends Assert {
    /**
     * The prefix of a test method.
     */
    protected static final String TEST_METHOD_PREFIX = "test";

    /**
     * The prefix of a begin test method.
     */
    protected static final String BEGIN_METHOD_PREFIX = "begin";

    /**
     * The prefix of an end test method.
     */
    protected static final String END_METHOD_PREFIX = "end";

    /**
     * The name of the method that is called before each test on the client
     * side (if it exists).
     */
    protected static final String CLIENT_GLOBAL_BEGIN_METHOD = "begin";

    /**
     * The name of the method that is called after each test on the client
     * side (if it exists).
     */
    protected static final String CLIENT_GLOBAL_END_METHOD = "end";

    /**
     * The logger (only used on the client side).
     */
    private Log logger;

    /**
     * Pure JUnit Test Case that we are wrapping (if any).
     */
    private Test wrappedTest;

    /**
     * The test we are delegating for.
     */
    private Test delegatedTest;

    /**
     * The protocol handler to use to execute the tests on the server side.
     */
    private ProtocolHandler protocolHandler;

    // Constructors ---------------------------------------------------------

    /**
     * @param theDelegatedTest the test we are delegating for
     * @param theWrappedTest the test being wrapped by this delegate (or null 
     *        if none)
     * @param theProtocolHandler the protocol handler to use to execute the 
     *        tests on the server side 
     */
    public ClientTestCaseCaller(Test theDelegatedTest, Test theWrappedTest, ProtocolHandler theProtocolHandler) {
        if (theDelegatedTest == null) {
            throw new IllegalStateException("The test object passed must not be null");
        }

        setDelegatedTest(theDelegatedTest);
        setWrappedTest(theWrappedTest);
        this.protocolHandler = theProtocolHandler;
    }

    // Public methods -------------------------------------------------------

    /**
     * Execute begin and end methods and calls the different 
     * {@link ProtocolHandler} lifecycle methods to execute the test
     * on the server side.
     * 
     * Note that this method is overriden from the JUnit 
     * {@link junit.framework.TestCase} class in order to prevent JUnit from 
     * calling the {@link junit.framework.TestCase#setUp()} and
     * {@link junit.framework.TestCase#tearDown()} methods on the client side.
     * instead we are calling the server redirector proxy and the setup and
     * teardown methods will be executed on the server side.
     *
     * @exception Throwable if any error happens during the execution of
     *            the test
     */
    public void runTest() throws Throwable {
        Request request = this.protocolHandler.createRequest();

        // Call the set up and begin methods to fill the request object
        callGlobalBeginMethod(request);
        callBeginMethod(request);

        // Run the server test
        ProtocolState state = this.protocolHandler.runTest(getDelegatedTest(), getWrappedTest(), request);

        // Call the end method
        Object response = callEndMethod(request, this.protocolHandler.createResponseObjectFactory(state));

        // call the tear down method
        callGlobalEndMethod(request, this.protocolHandler.createResponseObjectFactory(state), response);

        this.protocolHandler.afterTest(state);
    }

    /**
     * @return The logger used by the <code>TestCase</code> class and
     *         subclasses to perform logging.
     */
    public final Log getLogger() {
        return this.logger;
    }

    /**
     * Perform client side initializations before each test, such as
     * re-initializating the logger and printing some logging information.
     */
    public void runBareInit() {
        // We make sure we reinitialize The logger with the name of the
        // current extending class so that log statements will contain the
        // actual class name (that's why the logged instance is not static).
        this.logger = LogFactory.getLog(this.getClass());

        // Mark beginning of test on client side
        getLogger().debug("------------- Test: " + this.getCurrentTestName());
    }

    /**
     * Call the test case begin method.
     *
     * @param theRequest the request object to pass to the begin method.
     * @exception Throwable any error that occurred when calling the begin
     *            method for the current test case.
     */
    public void callBeginMethod(Request theRequest) throws Throwable {
        callGenericBeginMethod(theRequest, getBeginMethodName());
    }

    /**
     * Call the test case end method.
     * 
     * @param theRequest the request data that were used to open the
     *                   connection.
     * @param theResponseFactory the factory to use to return response objects.
     * @return the created Reponse object
     * @exception Throwable any error that occurred when calling the end method
     *         for the current test case.
     */
    public Object callEndMethod(Request theRequest, ResponseObjectFactory theResponseFactory) throws Throwable {
        return callGenericEndMethod(theRequest, theResponseFactory, getEndMethodName(), null);
    }

    /**
     * Call the global begin method. This is the method that is called before
     * each test if it exists. It is called on the client side only.
     *
     * @param theRequest the request object which will contain data that will
     *        be used to connect to the Cactus server side redirectors.
     * @exception Throwable any error that occurred when calling the method
     */
    public void callGlobalBeginMethod(Request theRequest) throws Throwable {
        callGenericBeginMethod(theRequest, CLIENT_GLOBAL_BEGIN_METHOD);
    }

    /**
     * Call the client tear down up method if it exists.
     *
     * @param theRequest the request data that were used to open the
     *                   connection.
     * @param theResponseFactory the factory to use to return response objects.
     * @param theResponse the Response object if it exists. Can be null in
     *        which case it is created from the response object factory
     * @exception Throwable any error that occurred when calling the method
     */
    private void callGlobalEndMethod(Request theRequest, ResponseObjectFactory theResponseFactory,
            Object theResponse) throws Throwable {
        callGenericEndMethod(theRequest, theResponseFactory, CLIENT_GLOBAL_END_METHOD, theResponse);
    }

    // Private methods ------------------------------------------------------

    /**
     * @param theWrappedTest the pure JUnit test that we need to wrap 
     */
    private void setWrappedTest(Test theWrappedTest) {
        this.wrappedTest = theWrappedTest;
    }

    /**
     * @param theDelegatedTest the test we are delegating for
     */
    private void setDelegatedTest(Test theDelegatedTest) {
        this.delegatedTest = theDelegatedTest;
    }

    /**
     * @return the wrapped JUnit test
     */
    private Test getWrappedTest() {
        return this.wrappedTest;
    }

    /**
     * @return the test we are delegating for
     */
    private Test getDelegatedTest() {
        return this.delegatedTest;
    }

    /**
     * @return the test on which we will operate. If there is a wrapped
     *         test then the returned test is the wrapped test. Otherwise we
     *         return the delegated test.
     */
    private Test getTest() {
        Test activeTest;
        if (getWrappedTest() != null) {
            activeTest = getWrappedTest();
        } else {
            activeTest = getDelegatedTest();
        }
        return activeTest;
    }

    /**
     * @return the name of the test method to call without the
     *         TEST_METHOD_PREFIX prefix
     */
    private String getBaseMethodName() {
        // Sanity check
        if (!getCurrentTestName().startsWith(TEST_METHOD_PREFIX)) {
            throw new RuntimeException(
                    "bad name [" + getCurrentTestName() + "]. It should start with [" + TEST_METHOD_PREFIX + "].");
        }

        return getCurrentTestName().substring(TEST_METHOD_PREFIX.length());
    }

    /**
     * @return the name of the test begin method to call that initialize the
     *         test by initializing the <code>WebRequest</code> object
     *         for the test case.
     */
    private String getBeginMethodName() {
        return BEGIN_METHOD_PREFIX + getBaseMethodName();
    }

    /**
     * @return the name of the test end method to call when the test has been
     *         run on the server. It can be used to verify returned headers,
     *         cookies, ...
     */
    private String getEndMethodName() {
        return END_METHOD_PREFIX + getBaseMethodName();
    }

    /**
     * Call a begin method which takes Cactus WebRequest as parameter.
     *
     * @param theRequest the request object which will contain data that will
     *        be used to connect to the Cactus server side redirectors.
     * @param theMethodName the name of the begin method to call
     * @exception Throwable any error that occurred when calling the method
     */
    private void callGenericBeginMethod(Request theRequest, String theMethodName) throws Throwable {
        // First, verify if a begin method exist. If one is found, verify if
        // it has the correct signature. If not, send a warning.
        Method[] methods = getTest().getClass().getMethods();

        for (int i = 0; i < methods.length; i++) {
            if (methods[i].getName().equals(theMethodName)) {
                TestCaseImplementChecker.checkAsBeginMethod(methods[i]);

                try {
                    methods[i].invoke(getTest(), new Object[] { theRequest });

                    break;
                } catch (InvocationTargetException e) {
                    e.fillInStackTrace();
                    throw e.getTargetException();
                } catch (IllegalAccessException e) {
                    e.fillInStackTrace();
                    throw e;
                }
            }
        }
    }

    /**
     * Call the global end method. This is the method that is called after
     * each test if it exists. It is called on the client side only.
     *
     * @param theRequest the request data that were used to open the
     *        connection.
     * @param theResponseFactory the factory to use to return response objects.
     * @param theMethodName the name of the end method to call
     * @param theResponse the Response object if it exists. Can be null in
     *        which case it is created from the response object factory
     * @return the created Reponse object
     * @exception Throwable any error that occurred when calling the end method
     *            for the current test case.
     */
    private Object callGenericEndMethod(Request theRequest, ResponseObjectFactory theResponseFactory,
            String theMethodName, Object theResponse) throws Throwable {
        Method methodToCall = null;
        Object paramObject = null;

        Method[] methods = getTest().getClass().getMethods();

        for (int i = 0; i < methods.length; i++) {
            if (methods[i].getName().equals(theMethodName)) {
                TestCaseImplementChecker.checkAsEndMethod(methods[i]);

                paramObject = theResponse;

                if (paramObject == null) {
                    Class[] parameters = methods[i].getParameterTypes();
                    try {
                        paramObject = theResponseFactory.getResponseObject(parameters[0].getName(), theRequest);
                    } catch (ClientException e) {
                        throw new ClientException("The method [" + methods[i].getName()
                                + "] has a bad parameter of type [" + parameters[0].getName() + "]", e);
                    }
                }

                // Has a method to call already been found ?
                if (methodToCall != null) {
                    fail("There can only be one method [" + methods[i].getName() + "] per test case. "
                            + "Test case [" + this.getCurrentTestName() + "] has two at least !");
                }

                methodToCall = methods[i];
            }
        }

        if (methodToCall != null) {
            try {
                methodToCall.invoke(getTest(), new Object[] { paramObject });
            } catch (InvocationTargetException e) {
                e.fillInStackTrace();
                throw e.getTargetException();
            } catch (IllegalAccessException e) {
                e.fillInStackTrace();
                throw e;
            }
        }

        return paramObject;
    }

    /**
     * @return the name of the current test case being executed (it corresponds
     *         to the name of the test method with the "test" prefix removed.
     *         For example, for "testSomeTestOk" would return "someTestOk".
     */
    private String getCurrentTestName() {
        return JUnitVersionHelper.getTestCaseName(getDelegatedTest());
    }
}