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

org.switchyard.test.SwitchYardTestKit Maven / Gradle / Ivy

/*
 * Copyright 2013 Red Hat Inc. and/or its affiliates and other contributors.
 *
 * Licensed 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.switchyard.test;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.custommonkey.xmlunit.XMLAssert;
import org.custommonkey.xmlunit.XMLUnit;
import org.jboss.logging.Logger;
import org.junit.Assert;
import org.switchyard.ExchangeHandler;
import org.switchyard.Service;
import org.switchyard.ServiceDomain;
import org.switchyard.SwitchYardException;
import org.switchyard.common.type.Classes;
import org.switchyard.common.type.classpath.ClasspathScanner;
import org.switchyard.common.xml.XMLHelper;
import org.switchyard.config.model.MergeScanner;
import org.switchyard.config.model.Model;
import org.switchyard.config.model.ModelPuller;
import org.switchyard.config.model.Models;
import org.switchyard.config.model.Scannable;
import org.switchyard.config.model.Scanner;
import org.switchyard.config.model.ScannerInput;
import org.switchyard.config.model.ScannerOutput;
import org.switchyard.config.model.composite.CompositeModel;
import org.switchyard.config.model.switchyard.SwitchYardModel;
import org.switchyard.config.model.switchyard.SwitchYardNamespace;
import org.switchyard.config.model.switchyard.v1.V1SwitchYardModel;
import org.switchyard.config.model.transform.TransformModel;
import org.switchyard.deploy.Activator;
import org.switchyard.deploy.ActivatorLoader;
import org.switchyard.deploy.ServiceDomainManager;
import org.switchyard.deploy.internal.AbstractDeployment;
import org.switchyard.deploy.internal.Deployment;
import org.switchyard.handlers.MessageTraceHandler;
import org.switchyard.metadata.InOnlyService;
import org.switchyard.metadata.InOutService;
import org.switchyard.metadata.ServiceInterface;
import org.switchyard.test.mixins.AbstractTestMixIn;
import org.switchyard.transform.BaseTransformer;
import org.switchyard.transform.Transformer;
import org.w3c.dom.Document;

/**
 * @author [email protected]
 */
public class SwitchYardTestKit {

    /**
     * Logger.
     */
    private static Logger _logger = Logger.getLogger(SwitchYardTestKit.class);
    /**
     * Constant for the {@link org.switchyard.test.SwitchYardTestCaseConfig#config()} default.
     */
    protected static final String NULL_CONFIG = "$$NULL_SW_CONFIG$$";

    /**
     * Class test instance.
     */
    private Object _testInstance;
    /**
     * Test configuration model.
     */
    private SwitchYardModel _configModel;
    /**
     * The deployment.
     */
    private AbstractDeployment _deployment;
    /**
     * Test Mix-Ins.
     */
    private Map, MixInEntry> _testMixInInstances =
        new LinkedHashMap, MixInEntry>();

    private List _activators;

    /**
     * Public default constructor.
     * @param testInstance The test instance.
     * @exception Exception Error initializing test kit.
     */
    public SwitchYardTestKit(Object testInstance) throws Exception {
        this._testInstance = testInstance;

        SwitchYardTestCaseConfig testCaseConfig = testInstance.getClass().getAnnotation(SwitchYardTestCaseConfig.class);

        if (testCaseConfig != null) {
            String config = testCaseConfig.config();
            if (config != null && !config.equals(NULL_CONFIG)) {
                InputStream is = getResourceAsStream(config);

                if (is == null) {
                    // Try the file system...
                    File file = new File(config);
                    if (file.isFile()) {
                        is = new FileInputStream(file);
                    }
                }

                if (is == null) {
                    Assert.fail("Failed to locate test configuration '" + config + "' on the classpath or project sub-directory. See the @" + SwitchYardTestCaseConfig.class.getSimpleName() + " annotation on test class '" + _testInstance.getClass().getName() + "'.");
                }

                try {
                    _configModel = createSwitchYardModel(is, createScanners(testCaseConfig), testCaseConfig.validate());
                } finally {
                    try {
                        is.close();
                    } catch (Throwable t) {
                        // just to keep checkstyle happy
                        t.getMessage();
                    }
                }
            }
            Class[] testMixIns = testCaseConfig.mixins();
            if (testMixIns == null) {
                // No MixIns...
                _logger.debug("No TestMixIns for test.");
            } else if (testMixIns.length == 1 && testMixIns[0] == NullMixIns.class) {
                // No MixIns...
                _logger.debug("No TestMixIns for test.");
            } else {
                for (Class mixIn : testMixIns) {
                    _testMixInInstances.put(mixIn, null);
                }
            }
        }
        createMixInInstances();
        initializeMixIns();
    }

    /**
     * invoke the methods annotated with {@link BeforeDeploy} on test class and deploy SwitchYard application.
     * 
     * @throws Exception failed to deploy
     */
    public void start() throws Exception {
        beforeDeploy();
        deploy();
    }


    /**
     * Cleanup.
     */
    public void cleanup() {
        undeploy();
        cleanupMixIns();
    }

    /**
     * Get the test class instance.
     * @return The test class instance.
     */
    public Object getTestInstance() {
        return _testInstance;
    }

    /**
     * Get the configuration model driving this test instance, if one exists.
     * 

* An abstract deployment is created if no configuration model is supplied on construction. * * @return The config model, or null if no config model was used to construct the TestCase instance. */ public SwitchYardModel getConfigModel() { return _configModel; } /** * Get the list of activators for the test. * @return Activator list. */ public List getActivators() { return _activators; } /** * Get the deployment instance associated with the test case. * @return The deployment instance associated with the test case. */ public AbstractDeployment getDeployment() { return _deployment; } /** * Get the ServiceDomain. * @return The service domain. */ public ServiceDomain getServiceDomain() { assertDeployed(); return _deployment.getDomain(); } /** * Creates a QName given this test kit's config model's targetNamespace + the specified localPart. * @param localPart the specified localPart * @return the QName */ public QName createQName(String localPart) { final String tns; if (_configModel != null) { CompositeModel composite = _configModel.getComposite(); tns = composite != null ? composite.getTargetNamespace() : _configModel.getTargetNamespace(); } else { tns = null; } return XMLHelper.createQName(tns, localPart); } /** * Register an IN_OUT Service. *

* Registers a {@link MockHandler} as the service handler. * * @param serviceName The Service name. * @return The {@link MockHandler} service handler. */ public MockHandler registerInOutService(String serviceName) { MockHandler handler = new MockHandler(); getServiceDomain().registerService(createQName(serviceName), new InOutService(), handler); return handler; } /** * Register an IN_OUT Service. * * @param serviceName The Service name. * @param serviceHandler The service handler. */ public void registerInOutService(String serviceName, ExchangeHandler serviceHandler) { getServiceDomain().registerService(createQName(serviceName), new InOutService(), serviceHandler); } /** * Register an IN_OUT Service. * * @param serviceName The Service name. * @param serviceHandler The service handler. * @param metadata Service interface. */ public void registerInOutService(String serviceName, ExchangeHandler serviceHandler, ServiceInterface metadata) { getServiceDomain().registerService(createQName(serviceName), metadata, serviceHandler); } /** * Register an IN_ONLY Service. *

* Registers a {@link MockHandler} as the fault service handler. * * @param serviceName The Service name. * @return The {@link MockHandler} service fault handler. */ public MockHandler registerInOnlyService(String serviceName) { MockHandler handler = new MockHandler(); getServiceDomain().registerService(createQName(serviceName), new InOnlyService(), handler); return handler; } /** * Register an IN_ONLY Service. * * @param serviceName The Service name. * @param serviceHandler The service handler. */ public void registerInOnlyService(String serviceName, ExchangeHandler serviceHandler) { getServiceDomain().registerService(createQName(serviceName), new InOnlyService(), serviceHandler); } /** * Replaces an existing service registration (e.g. reference binding) with a test handler using * the same contract as the existing service provider. If multiple services are registered with * the specified name, the first one found is used. Generally speaking, it's not a good idea * to use this method if you have multiple services registered with the same name. In that * situation, use the removeService() and registerService() methods instead. * @param name name of the service to replace * @return mock service handler representing the service provider * @throws SwitchYardException if a service with the specified name does not exist */ public MockHandler replaceService(String name) throws SwitchYardException { return replaceService(createQName(name)); } /** * Replaces an existing service registration (e.g. reference binding) with a test handler using * the same contract as the existing service provider. If multiple services are registered with * the specified name, the first one found is used. Generally speaking, it's not a good idea * to use this method if you have multiple services registered with the same name. In that * situation, use the removeService() and registerService() methods instead. * @param name name of the service to replace * @param handler implementation to use as the service provider * @throws SwitchYardException if a service with the specified name does not exist */ public void replaceService(String name, ExchangeHandler handler) throws SwitchYardException { replaceService(createQName(name), handler); } /** * Replaces an existing service registration (e.g. reference binding) with a test handler using * the same contract as the existing service provider. If multiple services are registered with * the specified name, the first one found is used. Generally speaking, it's not a good idea * to use this method if you have multiple services registered with the same name. In that * situation, use the removeService() and registerService() methods instead. * @param name name of the service to replace * @return mock service handler representing the service provider * @throws SwitchYardException if a service with the specified name does not exist */ public MockHandler replaceService(QName name) throws SwitchYardException { MockHandler handler = new MockHandler(); replaceService(name, handler); return handler; } /** * Replaces an existing service registration (e.g. reference binding) with a test handler using * the same contract as the existing service provider. If multiple services are registered with * the specified name, the first one found is used. Generally speaking, it's not a good idea * to use this method if you have multiple services registered with the same name. In that * situation, use the removeService() and registerService() methods instead. * @param name name of the service to replace * @param handler implementation to use as the service provider * @throws SwitchYardException if a service with the specified name does not exist */ public void replaceService(QName name, ExchangeHandler handler) throws SwitchYardException { List services = getServiceDomain().getServices(name); if (services.isEmpty()) { throw new SwitchYardException("Failed to replace service: " + name + ". No service is registered with that name."); } // select the service to replace Service replacedService = services.get(0); replacedService.unregister(); // add the replacement service getServiceDomain().registerService(name, replacedService.getInterface(), handler, replacedService.getServiceMetadata()); } /** * Removes all service providers from the domain with the specified name. * @param serviceName local part of a service QName */ public void removeService(String serviceName) { removeService(createQName(serviceName)); } /** * Removes all service providers from the domain with the specified name. * @param serviceName qualified name of the service */ public void removeService(QName serviceName) { for (Service service : getServiceDomain().getServices(serviceName)) { service.unregister(); } } /** * Add a Transformer instance. * @param transformer The transformer instance. */ public void addTransformer(Transformer transformer) { getServiceDomain().getTransformerRegistry().addTransformer(transformer); } /** * Create a new {@link Invoker} instance for invoking a Service in the test ServiceDomain. * @param serviceName The target Service name. * @return The invoker instance. */ public Invoker newInvoker(QName serviceName) { return new Invoker(getServiceDomain(), serviceName); } /** * Create a new {@link Invoker} instance for invoking a Service in the test ServiceDomain. * @param serviceName The target Service name. Can be a serialized {@link QName}. Can also * include the operation name e.g. "OrderManagementService.createOrder". * @return The invoker instance. */ public Invoker newInvoker(String serviceName) { return newInvoker(createQName(serviceName)); } /** * Create a new {@link Transformer} instance from the specified {@link org.switchyard.config.model.transform.TransformModel}. * @param transformModel The TransformModel. * @return The Transformer instance. */ public Transformer newTransformer(TransformModel transformModel) { return _deployment.getTransformerRegistryLoader().newTransformer(transformModel); } /** * Create a new {@link Transformer} instance from the specified {@link TransformModel} and * register it with the test ServiceDomain. * @param transformModel The TransformModel. * @return The Transformer instance. */ public Transformer registerTransformer(TransformModel transformModel) { if (transformModel.getFrom() == null || transformModel.getTo() == null) { Assert.fail("Invalid TransformModel instance. Must specify 'from' and 'to' data types."); } Transformer transformer = _deployment.getTransformerRegistryLoader().newTransformer(transformModel); if (transformer.getFrom() == null) { transformer = new TransformerWrapper(transformer, transformModel); } _deployment.getDomain().getTransformerRegistry().addTransformer(transformer); return transformer; } /** * Get the {@link TestMixIn} instances associated with this test instance. * @return The {@link TestMixIn} instances associated with this test instance. */ public List getMixIns() { List mixins = new ArrayList(); for (MixInEntry entry : _testMixInInstances.values()) { mixins.add(entry.getMixIn()); } return Collections.unmodifiableList(mixins); } /** * Get the "active" {@link TestMixIn} instance of the specified type. *

* This method can only be called from inside a test method. * * @param type The {@link TestMixIn} type, as specified in the {@link SwitchYardTestCaseConfig} annotation. * @param type {@link TestMixIn} type. * @return The {@link TestMixIn} instance. */ public T getMixIn(Class type) { if (_testMixInInstances == null || _testMixInInstances.isEmpty()) { Assert.fail("No TestMixIns specified on Test class instance. Use the @TestMixIns annotation."); } if (_testMixInInstances.size() != getMixIns().size()) { Assert.fail("TestMixIn instances only available during test method execution."); } MixInEntry mixIn = _testMixInInstances.get(type); if (mixIn == null) { Assert.fail("Required TestMixIn '" + type.getName() + "' is not specified on TestCase '" + _testInstance.getClass().getName() + "'."); } return type.cast(mixIn.getMixIn()); } /** * Returns required mixin dependencies. * * @param mixIn Mix in asking about dependencies. * @return Dependencies which are mandatory for mixin */ public Set getRequiredDependencies(TestMixIn mixIn) { return Collections.unmodifiableSet(_testMixInInstances.get(mixIn.getClass()).getRequiredDeps()); } /** * Returns optional mixin dependencies. * * @param mixIn Mix in asking about dependencies. * @return Dependencies which are not mandatory for mixin */ public Set getOptionalDependencies(TestMixIn mixIn) { return Collections.unmodifiableSet(_testMixInInstances.get(mixIn.getClass()).getOptionalDeps()); } /** * Finds a resource with a given name. *

* Searches relative to the implementing class definition. * * @param name Name of the desired resource * @return A {@link java.io.InputStream} object or null if no resource with this name is found. * * @see org.switchyard.common.type.Classes#getResourceAsStream(String,Class) */ public InputStream getResourceAsStream(String name) { try { return Classes.getResourceAsStream(name, _testInstance.getClass()); } catch (IOException ioe) { return null; } } /** * Read a classpath resource and return as a byte array. * @param path The path to the classpath resource. The specified path can be * relative to the test class' location on the classpath. * @return The resource as an array of bytes. */ public byte[] readResourceBytes(String path) { if (path == null) { Assert.fail("Resource 'path' not specified."); } InputStream resourceStream = getResourceAsStream(path); if (resourceStream == null) { Assert.fail("Resource '" + path + "' not found on classpath relative to test class '" + _testInstance.getClass().getName() + "'. May need to fix the relative path, or make the path absolute."); } ByteArrayOutputStream byteOutStream = new ByteArrayOutputStream(); try { byte[] readBuffer = new byte[128]; int readCount = 0; while ((readCount = resourceStream.read(readBuffer)) != -1) { byteOutStream.write(readBuffer, 0, readCount); } } catch (IOException e) { Assert.fail("Unexpected read error reading classpath resource '" + path + "'" + e.getMessage()); } finally { try { resourceStream.close(); } catch (IOException e) { Assert.fail("Unexpected exception closing classpath resource '" + path + "'" + e.getMessage()); } } return byteOutStream.toByteArray(); } /** * Read a classpath resource and return as a String. * @param path The path to the classpath resource. The specified path can be * relative to the test class' location on the classpath. * @return The resource as a String. */ public String readResourceString(String path) { try { return new String(readResourceBytes(path), "UTF-8"); } catch (UnsupportedEncodingException e) { Assert.fail("Unexpected exception reading classpath resource '" + path + "' as a String. Perhaps this resource is a binary resource that cannot be encoded as a String." + e.getMessage()); return null; // Keep the compiler happy. } } /** * Read a classpath resource and return as an XML DOM Document. * * @param path The path to the classpath resource. The specified path can be * relative to the test class' location on the classpath. * @return The resource as a Document. */ public Document readResourceDocument(String path) { try { DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); dbf.setNamespaceAware(true); DocumentBuilder db = dbf.newDocumentBuilder(); return db.parse(getResourceAsStream(path)); } catch (Exception e) { Assert.fail("Unexpected exception reading classpath resource '" + path + "' as a DOM Document." + e.getMessage()); return null; // Keep the compiler happy. } } /** * Load the SwitchYard configuration model specified by the configModel stream. * @param configModel The config model stream. * @return The SwitchYard config model. */ public SwitchYardModel loadSwitchYardModel(InputStream configModel) { return loadSwitchYardModel(configModel, true); } /** * Load the SwitchYard configuration model specified by the configModel stream. * @param configModel The config model stream. * @param validate Validate the model? * @return The SwitchYard config model. */ public SwitchYardModel loadSwitchYardModel(InputStream configModel, boolean validate) { return loadConfigModel(configModel, SwitchYardModel.class, validate); } /** * Load the configuration model specified by the configModel stream. * @param Model type. * @param configModel The config model stream. * @param modelType Model type. * @return The config model. */ public M loadConfigModel(InputStream configModel, Class modelType) { return loadConfigModel(configModel, modelType, true); } /** * Load the SwitchYard configuration model specified by the configModel stream. * @param Model type. * @param configModel The config model stream. * @param modelType Model type. * @param validate Validate the model? * @return The SwitchYard config model. */ public M loadConfigModel(InputStream configModel, Class modelType, boolean validate) { if (configModel == null) { throw new IllegalArgumentException("null 'configModel' arg."); } try { M pulledModel = new ModelPuller().pull(configModel); if (validate) { pulledModel.assertModelValid(); } return pulledModel; } catch (IOException e) { Assert.fail("Unexpected error building " + modelType.getSimpleName() + ": " + e.getMessage()); } finally { try { configModel.close(); } catch (IOException e) { Assert.fail("Unexpected error closing " + modelType.getSimpleName() + " stream: " + e.getMessage()); } } return null; } /** * Compare an XML string (e.g. a result) against a classpath resource. * @param xml The XML (as a String) to be compared against the XML in the specified * classpath resource. * @param resourcePath The path to the classpath resource against which the XML is to be * compared. The specified path can be relative to the test class' location on the classpath. */ public void compareXMLToResource(String xml, String resourcePath) { XMLUnit.setIgnoreWhitespace(true); try { XMLAssert.assertXMLEqual(readResourceString(resourcePath), xml); } catch (Exception e) { Assert.fail("Unexpected error performing XML comparison: " + e.getMessage()); } } /** * Compare an XML String (e.g. a result) against another String. * @param xml The XML (as a String) to be compared against the XML in the specified * classpath resource. * @param string The String against which the XML is to be * compared. */ public static void compareXMLToString(String xml, String string) { XMLUnit.setIgnoreWhitespace(true); try { XMLAssert.assertXMLEqual(string, xml); } catch (Exception e) { Assert.fail("Unexpected error performing XML comparison."); } } /** * Enables message tracing for the application under test. * @param doTrace true to enable message tracing, false to disable */ public void traceMessages(boolean doTrace) { getServiceDomain().setProperty(MessageTraceHandler.TRACE_ENABLED, true); } /** * invoke the methods annotated with {@link BeforeDeploy} on test class. */ private void beforeDeploy() throws Exception { Method[] publicMethods = _testInstance.getClass().getMethods(); for (Method method : publicMethods) { BeforeDeploy beforeAnno = method.getAnnotation(BeforeDeploy.class); if (beforeAnno != null) { method.invoke(_testInstance); } } } /** * Create and initialise the deployment. * @throws Exception creating the deployment. */ private final void deploy() throws Exception { _deployment = createDeployment(); ServiceDomain domain = new ServiceDomainManager().createDomain( ServiceDomainManager.ROOT_DOMAIN, _deployment.getConfig()); _activators = ActivatorLoader.createActivators(domain); SwitchYardTestCaseConfig testCaseConfig = _testInstance.getClass().getAnnotation(SwitchYardTestCaseConfig.class); if (testCaseConfig != null) { // Process includes... Collection includes = new HashSet(Arrays.asList(testCaseConfig.include())); if (!includes.isEmpty()) { Iterator activatorsIt = _activators.iterator(); while (activatorsIt.hasNext()) { Activator activator = activatorsIt.next(); // If the activator does not specify one of the include types, then remove it... if (!intersection(includes, activator.getActivationTypes())) { activatorsIt.remove(); } } } // Process excludes... Collection excludes = new HashSet(Arrays.asList(testCaseConfig.exclude())); if (!excludes.isEmpty()) { Iterator activatorsIt = _activators.iterator(); while (activatorsIt.hasNext()) { Activator activator = activatorsIt.next(); // If the activator specifies one of the exclude types, then remove it... if (intersection(excludes, activator.getActivationTypes())) { activatorsIt.remove(); } } } } _deployment.init(domain, _activators); mixInBefore(); _deployment.setFailOnMissingActivator(false); // It's OK to have a "missing" activator for a test, so we don't want to fail. _deployment.start(); } /** * Undeploy the deployment. */ private final void undeploy() { assertDeployed(); _deployment.stop(); mixInAfter(); _deployment.destroy(); } /** * Create the deployment instance. * @return The deployment instance. * @throws Exception creating the deployment. */ private AbstractDeployment createDeployment() throws Exception { if (_configModel != null) { return new Deployment(_configModel); } else { return new SimpleTestDeployment(); } } private void initializeMixIns() { for (TestMixIn mixIn : getMixIns()) { mixIn.initialize(); } } private void mixInBefore() { for (TestMixIn mixIn : getMixIns()) { mixIn.before(_deployment); } } private void mixInAfter() { // Apply after MixIns in reverse order... List mixins = new ArrayList(getMixIns()); Collections.reverse(mixins); for (TestMixIn mixIn : mixins) { mixIn.after(_deployment); } } private void cleanupMixIns() { // Destroy MixIns in reverse order... List mixins = new ArrayList(getMixIns()); Collections.reverse(mixins); for (TestMixIn mixIn : mixins) { mixIn.uninitialize(); } } private void createMixInInstances() { if (_testMixInInstances == null || _testMixInInstances.isEmpty()) { // No Mix-Ins... return; } Set> mixinClasses = new LinkedHashSet>( _testMixInInstances.keySet()); for (Class mixInType : mixinClasses) { createMixInRecursively(mixInType); } } private MixInEntry createMixInRecursively(Class mixInType) { if (_testMixInInstances.containsKey(mixInType) && _testMixInInstances.get(mixInType) != null) { return _testMixInInstances.get(mixInType); } Set requiredDeps = new LinkedHashSet(); Set optionalDeps = new LinkedHashSet(); // create dependencies first MixInDependencies dependencies = mixInType.getAnnotation(MixInDependencies.class); if (dependencies != null) { Class[] activeDependencies = dependencies.required(); if (activeDependencies != null && activeDependencies[0] != NullMixIns.class) { for (Class mixin : activeDependencies) { MixInEntry dependency = createMixInRecursively(mixin); requiredDeps.add(dependency.getMixIn()); _testMixInInstances.put(mixin, dependency); } } Class[] passiveDependencies = dependencies.optional(); if (passiveDependencies != null && passiveDependencies[0] != NullMixIns.class) { for (Class mixin : passiveDependencies) { if (_testMixInInstances.containsKey(mixin)) { MixInEntry dependency = createMixInRecursively(mixin); optionalDeps.add(dependency.getMixIn()); _testMixInInstances.put(mixin, dependency); } } } } TestMixIn testMixIn = newMixInInstance(mixInType, _testInstance); testMixIn.setTestKit(this); MixInEntry entry = new MixInEntry(testMixIn, requiredDeps, optionalDeps); // here is where whole trick is done - we must move created mixin after // it's dependencies, so we remove entry and add same instance once again _testMixInInstances.remove(mixInType); _testMixInInstances.put(mixInType, entry); return entry; } protected static T newMixInInstance(Class mixInType, Object testInstance) { Class testClass = testInstance.getClass(); Method[] methods = testClass.getDeclaredMethods(); // Check for a factory method for the MixIn type... for (Method method : methods) { int modifiers = method.getModifiers(); if (Modifier.isPublic(modifiers)) { if (method.getReturnType() == mixInType && method.getParameterTypes().length == 0) { try { if (Modifier.isStatic(modifiers)) { return mixInType.cast(method.invoke(null)); } else { return mixInType.cast(method.invoke(testInstance)); } } catch (Exception e) { e.printStackTrace(); Assert.fail("Failed to create instance of TestMixIn type " + mixInType.getName() + ". Error invoking the MixIn factory method '" + method.getName() + "': " + e.getMessage()); return null; } } } } try { return mixInType.newInstance(); } catch (Exception e) { e.printStackTrace(); Assert.fail("Failed to create instance of TestMixIn type " + mixInType.getName() + " via public default constructor: " + e.getMessage()); return null; } } private SwitchYardModel createSwitchYardModel(InputStream configModel, List> scanners, boolean validate) { Assert.assertNotNull("Test 'configModel' is null.", configModel); final SwitchYardModel returnModel; try { SwitchYardModel model = loadSwitchYardModel(configModel, false); ClassLoader classLoader = _testInstance.getClass().getClassLoader(); if (scanners != null && !scanners.isEmpty() && classLoader instanceof URLClassLoader) { String uri = model.getModelRootNamespace(); SwitchYardNamespace ns = SwitchYardNamespace.fromUri(uri); MergeScanner merge_scanner = new MergeScanner(true, scanners); List scanURLs = getScanURLs((URLClassLoader)classLoader); ScannerInput scanner_input = new ScannerInput() .setSwitchyardNamespace(ns).setCompositeName(model.getName()).setURLs(scanURLs); V1SwitchYardModel scannedModel = merge_scanner.scan(scanner_input).getModel(); returnModel = Models.merge(scannedModel, model, false); } else { returnModel = model; } if (validate) { returnModel.assertModelValid(); } return returnModel; } catch (java.io.IOException ioEx) { throw new SwitchYardException("Failed to read switchyard config.", ioEx); } } private void assertDeployed() { if (_deployment == null) { Assert.fail("TestCase deployment not yet deployed. You may need to make an explicit call to the deploy() method."); } } private List> createScanners(SwitchYardTestCaseConfig testCaseConfig) { List> scanners = new ArrayList>(); if (testCaseConfig != null) { Class[] scannerClasses = testCaseConfig.scanners(); if (scannerClasses == null) { // No Scanners _logger.debug("No Scanners for test."); } else if (scannerClasses.length == 1 && scannerClasses[0] == NullScanners.class) { // No Scanners _logger.debug("No Scanners for test."); } else { for (Class scannerClass : scannerClasses) { try { scanners.add(scannerClass.newInstance()); } catch (Exception e) { Assert.fail("Exception creating instance of Scanner class '" + scannerClass.getName() + "': " + e.getMessage()); } } } } return scanners; } private List getScanURLs(URLClassLoader classLoader) { URL[] classPathURLs = classLoader.getURLs(); List scanURLs = new ArrayList(); // Only scan directories. Above all, we want to make sure we don't // start scanning JDK jars etc... for (URL classpathURL : classPathURLs) { try { File file = ClasspathScanner.toClassPathFile(classpathURL); if (file.isDirectory()) { scanURLs.add(classpathURL); } } catch (IOException e) { Assert.fail("Failed to convert classpath URL '" + classpathURL + "' to a File instance."); } } // Temp hack to work around SWITCHYARD-343 (https://issues.jboss.org/browse/SWITCHYARD-343)... if (scanURLs.isEmpty()) { try { scanURLs.add(new File("target/test-classes").toURI().toURL()); scanURLs.add(new File("target/classes").toURI().toURL()); } catch (MalformedURLException e) { Assert.fail("Unexpected exception adding target test classes folders to test scan URLs: " + e.getMessage()); } } return scanURLs; } private boolean intersection(Collection set1, Collection set2) { if (set1.isEmpty() || set2.isEmpty()) { return false; } Collection set1Copy = new HashSet(set1); set1Copy.removeAll(set2); // If entries were removed from the set then we have an intersect, otherwise we don't... return (set1Copy.size() < set1.size()); } @Scannable(false) private static final class TransformerWrapper extends BaseTransformer { private Transformer _transformer; private TransformModel _transformModel; private TransformerWrapper(Transformer transformer, TransformModel transformModel) { this._transformer = transformer; this._transformModel = transformModel; } @Override public Object transform(Object from) { return _transformer.transform(from); } @Override public QName getFrom() { return _transformModel.getFrom(); } @Override public QName getTo() { return _transformModel.getTo(); } } /** * Hidden marker type to provide a valid NULL * Scanners configuration for {@link SwitchYardTestCaseConfig}. */ protected static final class NullScanners implements Scanner { @Override public ScannerOutput scan(ScannerInput scannerInput) throws IOException { return null; } } /** * Hidden marker type to provide a valid NULL * TestMixIns configuration for {@link SwitchYardTestCaseConfig}. */ protected static final class NullMixIns extends AbstractTestMixIn { } /** * Helper class to keep mixin and it's dependencies. * */ protected static class MixInEntry { private final TestMixIn _mixin; private Set _requiredDeps; private Set _optionalDeps; public MixInEntry(TestMixIn mixin, Set requiredDeps, Set optionalDeps) { this._mixin = mixin; this._requiredDeps = requiredDeps; this._optionalDeps = optionalDeps; } public TestMixIn getMixIn() { return _mixin; } public Set getRequiredDeps() { return _requiredDeps; } public Set getOptionalDeps() { return _optionalDeps; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy