Package org.castor.xmlctf

Source Code of org.castor.xmlctf.XMLTestCase

/*
* Redistribution and use of this software and associated documentation
* ("Software"), with or without modification, are permitted provided
* that the following conditions are met:
*
* 1. Redistributions of source code must retain copyright
*    statements and notices.  Redistributions must also contain a
*    copy of this document.
*
* 2. Redistributions in binary form must reproduce the
*    above copyright notice, this list of conditions and the
*    following disclaimer in the documentation and/or other
*    materials provided with the distribution.
*
* 3. The name "Exolab" must not be used to endorse or promote
*    products derived from this Software without prior written
*    permission of Intalio, Inc.  For written permission,
*    please contact info@exolab.org.
*
* 4. Products derived from this Software may not be called "Exolab"
*    nor may "Exolab" appear in their names without prior written
*    permission of Intalio, Inc. Exolab is a registered
*    trademark of Intalio, Inc.
*
* 5. Due credit should be given to the Exolab Project
*    (http://www.exolab.org/).
*
* THIS SOFTWARE IS PROVIDED BY INTALIO, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
* NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
* INTALIO, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Copyright 2001-2003 (C) Intalio, Inc. All Rights Reserved.
*
* $Id: XMLTestCase.java 6787 2007-01-29 06:00:49Z ekuns $
*/
package org.castor.xmlctf;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.InputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.Method;
import java.util.Enumeration;
import java.util.LinkedList;
import java.util.List;

import junit.framework.TestCase;

import org.castor.xmlctf.util.CTFUtils;
import org.exolab.castor.mapping.Mapping;
import org.exolab.castor.tests.framework.testDescriptor.CallMethod;
import org.exolab.castor.tests.framework.testDescriptor.Configuration;
import org.exolab.castor.tests.framework.testDescriptor.ConfigurationType;
import org.exolab.castor.tests.framework.testDescriptor.FailureType;
import org.exolab.castor.tests.framework.testDescriptor.ListenerType;
import org.exolab.castor.tests.framework.testDescriptor.UnitTestCase;
import org.exolab.castor.tests.framework.testDescriptor.Value;
import org.exolab.castor.tests.framework.testDescriptor.types.FailureStepType;
import org.exolab.castor.tests.framework.testDescriptor.types.TypeType;
import org.exolab.castor.util.NestedIOException;
import org.exolab.castor.xml.MarshalException;
import org.exolab.castor.xml.MarshalListener;
import org.exolab.castor.xml.Marshaller;
import org.exolab.castor.xml.Unmarshaller;
import org.exolab.castor.xml.XMLContext;
import org.xml.sax.InputSource;

/**
* This class encapsulates all the common logic to run the test patterns for
* Castor XML. Basically it handle the marshalling/marshalling/comparing. This
* is used to factor the common code for the source generator test and the
* mapping/introspection tests as only the setup differ in the test patterns.
* <p>
* This class is not complete and expects to be extended.
*
* @author <a href="mailto:gignoux@kernelcenter.org">Sebastien Gignoux</a>
* @author <a href="mailto:blandin@intalio.com">Arnaud Blandin</a>
* @version $Revision: 6787 $ $Date: 2006-04-26 15:14:53 -0600 (Wed, 26 Apr 2006) $
*/
public abstract class XMLTestCase extends TestCase {

    /** True if we desire a lot of info on what happen. */
    public static boolean _verbose;
    /** True if we dump the stack trace for any exception that occurs during testing. */
    protected static boolean _printStack;

    static {
        String v = System.getProperty(TestCaseAggregator.VERBOSE_PROPERTY);
        _verbose = (v!=null && v.equals(Boolean.TRUE.toString()));

        v = System.getProperty(TestCaseAggregator.PRINT_STACK_TRACE);
        _printStack = (v!=null && v.equals(Boolean.TRUE.toString()));
    }

    /** Name of the test suite to which this test belongs. */
    protected String               _suiteName;
    /** The name of the root class for this test.  Must be set by a concrete class
     * if a test with a random object is used. */
    protected String               _rootClassName;
    /** The root class for this test.  Must be set by a concrete class. */
    protected Class                _rootClass;
    /** If true, the dumpFields() function has been implemented in the root class. */
    protected boolean              _hasDump;
    /** The name of the mapping file to use in a Marshalling Framework Test Case.
     * Must be set by a concrete class if a test with a reference document is used. */
    protected Mapping              _mapping;
    /** A listener for marshalling. */
    protected Object               _listener;
    /** Type of listener test -- Marshal, Unmarshal or Both. */
    protected TypeType             _listenerType;
    /** Gold file for listener. */
    protected String               _listenerGoldFile;
    /** The Configuration the Marshalling Framework. */
    protected Configuration        _configuration;
    /** Name of this test. */
    protected final String         _name;
    /** The unit test case this class represent. */
    protected final UnitTestCase   _unitTest;
    /** Place where the temporary file have to be put. */
    protected final File           _outputRootFile;
    /** True if the test needs to be skipped. */
    protected final boolean        _skip;
    /** The failure object that is not null if the test is expected to fail. */
    protected final FailureType    _failure;
    /** Used only to retrieve the classloader. */
    protected final CastorTestCase _test;
    /** The internal Castor XML context. */
    private XMLContext _xmlContext;

    /**
     * Instantiate a new XMLTestCase with the given name.
     *
     * @param name the name of this test case.
     */
    public XMLTestCase(final String name) {
        super(name);
        _name = cleanup(name);
        throw new IllegalArgumentException("You cannot use the name-only constructor");
    }

    /**
     * Instantiates a new test case that represents the given UnitTestCase.
     *
     * @param test the reference to the jar/directory
     * @param unit the UnitTestCase that wraps the configuration for this XML Test case.
     */
    public XMLTestCase(final CastorTestCase test, final UnitTestCase unit) {
        super(unit.getName());
        _name           = cleanup(unit.getName());
        _unitTest       = unit;
        _skip           = unit.getSkip();
        _failure        = unit.getFailure();
        _test           = test;
        _outputRootFile = test.getOutputRootFile();
        _xmlContext     = null;
    }

    /**
     * Instantiates a new test cases that has the same configuration of the given
     * test case.
     *
     * @param name the name of the test case
     * @param tc the XML test case that hold the configuration for this test case
     */
    public XMLTestCase(final String name, final XMLTestCase tc) {
        super(name);
        _name           = cleanup(tc._name);
        _unitTest       = tc._unitTest;
        _outputRootFile = tc._outputRootFile;
        _skip           = tc._skip;
        _failure        = tc._failure;
        _test           = tc._test;
        _xmlContext     = null;
    }
   
    protected abstract void setUp() throws Exception;
   
    protected abstract void tearDown() throws Exception;

    public void setXMLContext(XMLContext xmlContext) {
        _xmlContext = xmlContext;
    }
   
    public XMLContext getXMLContext() {
        return _xmlContext;
    }

    /**
     * Returns a version of the input name that is suitable for JUnit test case
     * or test suite use. Apparently, JUnit truncates test case names after
     * encountering certain characters, so we scrub those characters from the
     * test case or test suite name.
     * <p>
     * We also translate all whitespace to blanks, remove all leading and
     * trailing whitespace, and collapse consecutive whitespace to a single
     * blank.
     *
     * @param name
     *            the input name
     * @return a name suitable for JUnit test case or test suite use.
     */
    static String cleanup(final String name) {
        return name.replaceAll("[\\*()\\t\\n\\r\\f]", " ").replaceAll(" {2,}", " ").trim();
    }

    /**
     * Sets the name of the test suite to which this test case belongs to.
     *
     * @param suiteName the name of the test suite.
     *
     */
    public void setTestSuiteName(final String suiteName) {
        _suiteName = cleanup(suiteName);
    }

    /**
     * Returns the name of the test suite to which this test case belongs to.
     * @return the name of the test suite to which this test case belongs to.
     */
    public String getTestSuiteName() {
        return _suiteName;
    }

    /**
     * Called when a test case throws an Exception. Returns true if we expected
     * this test to fail (and if this was the correct Exception class, if one is
     * specified, and if this was the correct test step for the failure to
     * occur, if one was specified).
     *
     * @param exception
     *            the Exception that was thrown
     * @param checkStep
     *            the test step that threw an Exception
     * @return true if we pass the test (because we expected this Exception)
     */
    protected boolean checkExceptionWasExpected(final Exception exception, final FailureStepType checkStep) {
        if (_printStack) {
            exception.printStackTrace();
        }
        // If we're not expecting any failure, then we're not expecting any Exception
        if (_failure == null || !_failure.getContent()) {
            return false;
        }
        if (checkStep == null) {
            fail("CTF coding failure ... checkStep should never be null");
        }

        // We expect failure.  Is this the failure we expected?

        String          exceptionName = _failure.getException();
        FailureStepType expectedStep  = _failure.getFailureStep();

        // If no specific step or Exception, then any failure is OK
        if (exceptionName == null && expectedStep == null) {
            return true;
        }

        // If we're expecting an exception, make sure we got the right one
        Exception ex = exception;
        if (ex instanceof NestedIOException) {
            ex = ((NestedIOException)ex).getException();
        }

        if (exceptionName != null) {
            try {
                Class expected = Class.forName(exceptionName);
                if (!expected.isAssignableFrom(ex.getClass())) {
                    String complaint = "Received Exception: '" + ex.getClass().getName()
                            + ": " + ex + "' but expected: '" + exceptionName + "'.";

                    StringWriter sw = new StringWriter();
                    PrintWriter pw = new PrintWriter(sw);
                    pw.print(complaint);
                    if (_verbose) {
                        pw.println("Stacktrace for the Exception that was thrown:");
                        ex.printStackTrace(pw);
                    }
                    pw.flush();
                    System.out.println(sw.toString());

                    fail(complaint);
                }

            } catch (ClassNotFoundException cnfex) {
                fail("The Exception specified: '" + exceptionName + "' cannot be found in the CLASSPATH");
            }
        }

        // If we're expecting to fail at a specific step, make sure this is that step
        if (expectedStep != null && !expectedStep.equals(checkStep)) {
            fail("We expected to fail at test step '" + expectedStep.toString()
                 + "' but actually failed at step '" + checkStep.toString() + "'");
        }

        // We got the expected failure!  Return true.
        return true;
    }

    /**
     * Marshals the object with the configuration of the test.
     *
     * @param object the object to marshall.
     * @param fileName the name of the file where to marshall the object.
     * @return a file containing marshalled output.
     */
    protected File testMarshal(final Object object, final String fileName) {
        verbose("--> Marshaling to: '" + fileName + "'");

        File marshalOutput = new File(_outputRootFile, fileName);

        try {
            Marshaller marshaller = createMarshaler(marshalOutput);
            marshaller.marshal(object);
        } catch (Exception e) {
            if (!checkExceptionWasExpected(e, FailureStepType.MARSHAL_TO_DISK)) {
                fail("Exception marshaling to disk " + e);
            }
            return null;
        }

        if (_failure != null && _failure.getContent() && _failure.getFailureStep() != null &&
             _failure.getFailureStep().equals(FailureStepType.MARSHAL_TO_DISK)) {
            fail("Unmarshaling was expected to fail, but succeeded");
        }
        return marshalOutput;
    }

    private Marshaller createMarshaler(final File marshalOutput) throws Exception {
        getXMLContext().getInternalContext().getXMLClassDescriptorResolver().cleanDescriptorCache();
        Marshaller marshaller = getXMLContext().createMarshaller();
        marshaller.setWriter(new FileWriter(marshalOutput));

        //-- Configuration for marshaler?  Use config from unit test case if available
        Configuration config = _unitTest.getConfiguration();
        if (config == null) {
            config = _configuration;
        }

        if (config != null) {
            ConfigurationType marshal = config.getMarshal();
            List returnValues = invokeEnumeratedMethods(marshaller, marshal);
            returnValues.clear(); // We don't care about the return values
        }//-- config != null

        if (_mapping != null) {
            marshaller.setMapping(_mapping);
        }

        if (_listener != null && _listener instanceof MarshalListener
                && _listenerType != TypeType.UNMARSHAL) {
            marshaller.setMarshalListener((MarshalListener)_listener);
        }
        return marshaller;
    }

    /**
     * Unmarshals the given file with the configuration of that test. If we
     * unmarshal null, complain and fail the test case.
     *
     * @param file the file containing the xml document to unmarshall.
     * @return the result of the unmarshalling of the given file.
     * @throws Exception if anything goes wrong during the test
     */
    protected Object testUnmarshal(final File file) throws Exception {
        verbose("--> Unmarshaling '" + file.getName() + "'\n");
        InputStream inputStream = new FileInputStream(file);
        Object unmarshaledObject = testUnmarshal(inputStream);
        inputStream.close();
        assertNotNull("Unmarshaling '" + file.getName() + "' results in a NULL object.",
                      unmarshaledObject);
        return unmarshaledObject;
    }

    /**
     * Unmarshals the given input stream with the configuration of that test.
     *
     * @param stream
     *            the input stream containing the xml document to unmarshall.
     * @return the result of the unmarshalling of the given file, null if
     *         anything went wrong.
     * @throws Exception if anything goes wrong during the test
     */
    protected Object testUnmarshal(final InputStream stream) throws Exception {
        Object result = null;
        final Unmarshaller unmar = createUnmarshaler();
        result = unmar.unmarshal(new InputSource(stream));
        return result;
    }

    private Unmarshaller createUnmarshaler() throws Exception {
        //-- Configuration for unmarshaler?  Use config from unit test case if available
        Configuration config = _unitTest.getConfiguration();
        if (config == null) {
            config = _configuration;
        }

        final Unmarshaller unmar;
        if (_mapping != null) {
//J            unmar = new Unmarshaller(_mapping);
            unmar = getXMLContext().createUnmarshaller();
            unmar.setMapping(_mapping);
        } else {
            if (_test.getClassLoader() != null) {
//J                unmar = new Unmarshaller(_rootClass, _test.getClassLoader());
                unmar = getXMLContext().createUnmarshaller();
                unmar.setClassLoader(_test.getClassLoader());
                unmar.setClass(_rootClass);
            } else {
//J                unmar = new Unmarshaller(_rootClass);
                unmar = getXMLContext().createUnmarshaller();
                unmar.setClass(_rootClass);
            }
        }

        if (_listener != null
                && _listener instanceof org.exolab.castor.xml.UnmarshalListener
                && _listenerType != TypeType.MARSHAL) {
            unmar.setUnmarshalListener((org.exolab.castor.xml.UnmarshalListener)_listener);
        }

        if (_listener != null
                && _listener instanceof org.castor.xml.UnmarshalListener
                && _listenerType != TypeType.MARSHAL) {
            unmar.setUnmarshalListener((org.castor.xml.UnmarshalListener)_listener);
        }

        unmar.setDebug(_verbose);

        if (config != null) {
            ConfigurationType unmarshal = config.getUnmarshal();
            List returnValues = invokeEnumeratedMethods(unmar, unmarshal);
            returnValues.clear(); // We don't care about the return values
        }

        return unmar;
    }

    /**
     * Invokes all requested methods on the object we are supplied.  Keeps track
     * of the objects returned from each invocation and returns a List containing
     * all of the return values.  Some of the return values may be null.  This
     * is not an error!
     *
     * @param objectInvoked the object on which to invoke the methods
     * @param config configuration object listing the methods we are to call
     * @throws java.lang.Exception if anything goes wrong during the test
     */
    protected List invokeEnumeratedMethods(final Object objectInvoked, final ConfigurationType config)
                                                                  throws java.lang.Exception {
        final List returnValues = new LinkedList();

        if (config == null) {
            return returnValues;
        }

        Enumeration methods = config.enumerateCallMethod();
        //-- For each method defined, we invoke it on marshaller just created.
        while (methods.hasMoreElements()) {
            CallMethod method = (CallMethod) methods.nextElement();
            //-- search for the method to invoke
            Method toBeinvoked = getInvokeMethod(objectInvoked.getClass(), method.getName(), method.getValue());
            //-- construct the objects representing the arguments of the method
            Object[] arguments = getArguments(method.getValue());
            //-- Invoke the method and keep a copy of the returned object
            returnValues.add(toBeinvoked.invoke(objectInvoked, arguments));
        }

        return returnValues;
    }

    /**
     * Initialize listeners for marshalling/unmarshalling
     *
     * @param listener the listener to initialize
     * @throws ClassNotFoundException
     * @throws IllegalAccessException
     * @throws InstantiationException
     */
    protected void initializeListeners(final ListenerType listener) throws ClassNotFoundException,
                                                                     IllegalAccessException,
                                                                     InstantiationException {
        String listenerClassName = listener.getClassName();
        if (listenerClassName == null || listenerClassName.length() == 0) {
            throw new IllegalArgumentException("ClassName must be provided for Listener element.");
        }

        final Class listenerClass;
        if (_test.getClassLoader() != null) {
            listenerClass = _test.getClassLoader().loadClass(listenerClassName);
        } else {
            listenerClass = Thread.currentThread().getContextClassLoader().loadClass(listenerClassName);
        }

        Object o = listenerClass.newInstance();
        if (o instanceof org.castor.xml.UnmarshalListener ||
                o instanceof org.exolab.castor.xml.UnmarshalListener ||
                o instanceof MarshalListener) {
            _listener = o;
        } else {
            _listener = null;
        }

        _listenerGoldFile = listener.getGoldFile();
        _listenerType = listener.getType();
    }

    /**
     * Returns an instance of the object model hardcoded in the given
     * ObjectModelBuilder.
     *
     * @param builderName the name of the class used as a builder
     * @return an instance of the object model hardcoded in the given
     *         ObjectModelBuilder.
     * @throws java.lang.Exception if anything goes wrong during the test
     */
    protected Object buildObjectModel(final String builderName) throws java.lang.Exception {
        Class builderClass = null;
        if (_test.getClassLoader() != null) {
            builderClass = _test.getClassLoader().loadClass(builderName);
        } else {
            builderClass = this.getClass().getClassLoader().loadClass(builderName);
        }
        ObjectModelBuilder builder = (ObjectModelBuilder)builderClass.newInstance();
        return builder.buildInstance();
    }

    private Method getInvokeMethod(final Class dataBinder, final String methodName,
                                   final Value[] values)
                                 throws ClassNotFoundException, NoSuchMethodException {
        if (methodName == null) {
            throw new IllegalArgumentException("The name of the method to invoke is null");
        }
        Class[] argumentsClass = null;

        //-- the value object represents the arguments of the method if any
        if (values != null && values.length > 0) {
            argumentsClass = new Class[values.length];
            for (int i = 0; i < values.length; i++) {
                Value value = values[i];
                argumentsClass[i] = CTFUtils.getClass(value.getType(), _test.getClassLoader());
            }
        }
        return dataBinder.getMethod(methodName, argumentsClass);
    }

    private Object[] getArguments(final Value[] values) throws ClassNotFoundException, MarshalException {
        Object[] result = new Object[values.length];
        for (int i = 0; i < values.length; i++) {
            Value value = values[i];
            result[i] = CTFUtils.instantiateObject(value.getType(), value.getContent(), _test.getClassLoader());
        }
        return result;
    }

    /**
     * print the message if in verbose mode.
     * @param message the message to print
     */
    protected void verbose(final String message) {
        if (_verbose) {
            System.out.println(message);
        }
    }

}
TOP

Related Classes of org.castor.xmlctf.XMLTestCase

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.