Package com.canoo.webtest.extension.applet

Source Code of com.canoo.webtest.extension.applet.AppletRunnerStep

// Copyright � 2002-2005 Canoo Engineering AG, Switzerland.
package com.canoo.webtest.extension.applet;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Iterator;
import java.util.Map;

import javax.xml.xpath.XPathExpressionException;

import junit.framework.TestCase;

import org.apache.commons.codec.DecoderException;
import org.apache.commons.httpclient.HostConfiguration;
import org.apache.commons.logging.LogFactory;
import org.apache.log4j.Logger;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.taskdefs.Execute;
import org.apache.tools.ant.taskdefs.LogStreamHandler;
import org.apache.tools.ant.taskdefs.Taskdef;
import org.apache.tools.ant.types.Commandline;
import org.apache.tools.ant.types.CommandlineJava;
import org.apache.tools.ant.types.Environment;
import org.apache.tools.ant.types.Path;
import org.netbeans.jemmy.Scenario;
import org.xml.sax.SAXException;

import com.canoo.webtest.ant.WebtestTask;
import com.canoo.webtest.boundary.AppletRunnerStepBoundary;
import com.canoo.webtest.boundary.UrlBoundary;
import com.canoo.webtest.engine.Context;
import com.canoo.webtest.engine.StepExecutionException;
import com.canoo.webtest.engine.StepFailedException;
import com.canoo.webtest.extension.applet.runner.AppletRunner;
import com.canoo.webtest.steps.request.AbstractTargetAction;
import com.canoo.webtest.steps.store.StoreCookie;
import com.canoo.webtest.util.FileUtil;
import com.gargoylesoftware.htmlunit.Page;
import com.gargoylesoftware.htmlunit.WebRequestSettings;
import com.gargoylesoftware.htmlunit.html.HtmlElement;
import com.gargoylesoftware.htmlunit.html.HtmlPage;

/**
* @author Denis N. Antonioli
* @webtest.step category="Extension"
* name="appletRunner"
* description="Runs and tests applets from inside webtest. If the applet triggers the load of a new page in the
* specified target, then this page becomes the current one."
*/
public class AppletRunnerStep extends AbstractTargetAction {
    private static final Logger LOG = Logger.getLogger(AppletRunnerStep.class);
    public static final String PROTOCOL_HANDLER_LIST = "java.protocol.handler.pkgs";
    /**
     * The package that is parent to all the necessary StreamHandler.
     * We <em>do not</em> uses reflection to get this value,
     * because we don't want any of these classes loaded in webtest jvm.
     */
    public static final String RUNNER_PACKAGE = "com.canoo.webtest.extension.applet.runner";
    /**
     * The name of a class in the clover jar. Uses this constant to find the jar file on the classpath,
     * if it is present.
     */
    public static final String A_CLOVER_CLASS = "com.cenqua.clover.CloverInstr";
    static final String LOG4J_DEFAULT_INIT_OVERRIDES = "log4j.defaultInitOverride";
    static final String LOG4J_CONFIGURATION = "log4j.configuration";
    static final Integer ZERO = new Integer(0);

    private String fTarget;
    private String fXPath;
    private String fScenario;
    private String fScenarioLocation;
    private final Path fScenarioLocationPath = new Path(null);
    private final ParameterSet fParameters = new ParameterSet();
    private final CommandlineJava fCommandline = new CommandlineJava();

    {
        setupLog4j(fCommandline);
        setupClasspath(fCommandline);
    }

    public String getScenarioLocation() {
        return fScenarioLocation;
    }

    /**
     * @param scenarioLocation
     * @webtest.parameter required="no"
     * description="The location of the scenario class."
     */
    public void setScenarioLocation(String scenarioLocation) {
        fScenarioLocation = scenarioLocation;
    }

    public String getScenario() {
        return fScenario;
    }

    /**
     * @param scenario
     * @webtest.parameter required="yes"
     * description="The name of the test scenario class."
     */
    public void setScenario(String scenario) {
        fScenario = scenario;
    }

    /**
     * @param xpath
     * @webtest.parameter required="yes"
     * description="The XPath identifying the applet element to start."
     */
    public void setXpath(String xpath) {
        fXPath = xpath;
    }

    public String getXpath() {
        return fXPath;
    }

    public String getTarget() {
        return fTarget;
    }

    /**
     * @param target
     * @webtest.parameter required="no"
     * description="A target to follow after the applet ends."
     */
    public void setTarget(String target) {
        fTarget = target;
    }

    public void setProject(Project p) {
        super.setProject(p);
        fScenarioLocationPath.setProject(getProject());
    }

    protected void verifyParameters() {
        super.verifyParameters();
        nullParamCheck(fXPath, "xpath");
        nullParamCheck(fScenario, "scenario");
        if (fScenarioLocation != null) {
            fScenarioLocationPath.addExisting(new Path(getProject(), fScenarioLocation));
        }
        nullResponseCheck();
    }

    /**
     * Adds the nested parameters children
     */
    protected void addComputedParameters(final Map map)
    {
        for (final Iterator iterator = fParameters.iterator(); iterator.hasNext();) {
            final Object obj = iterator.next();
            if (obj instanceof Parameter) {
                final Parameter param = (Parameter) obj;
                map.put(param.getName(), param.getValue());
            } else {
              final ParameterRef paramRef = (ParameterRef) obj;
                map.put(paramRef.getRegex(), paramRef.getPropertyType());
            }
        }
    }

    /**
     * @deprecated Use {@link #addParameter(Parameter)}.
     */
    public void addParam(final Parameter param) {
        addParameter(param);
    }

    /**
     * @param param
     * @webtest.nested.parameter required="no"
     * description="A parameter for the applet scenario."
     */
    public void addParameter(final Parameter param) {
        fParameters.add(param);
    }

    /**
     * @param param
     * @webtest.nested.parameter required="no"
     * description="A parameter for the applet scenario."
     */
    public void addParameterRef(final ParameterRef param) {
        fParameters.add(param);
    }

    Iterator getParameters() {
        return fParameters.iterator();
    }

    protected Page findTarget() throws IOException, SAXException, XPathExpressionException {
        fParameters.expandProperties(this);

        AppletPluginArguments appletPluginArguments = setUpAppletPluginArguments();
        AppletPluginResults apr = runApplet(appletPluginArguments);
        return findTargetWithResults(apr, getContext());
    }

    protected String getLogMessageForTarget() {
        return "by applet showDocument";
    }

    protected Page findTargetWithResults(AppletPluginResults apr, Context context) throws IOException, SAXException {
        if (fTarget != null) {
            final Map allFrames = apr.getFrames();
            if (allFrames.containsKey(fTarget)) {
                final URL url = (URL) allFrames.get(fTarget);
                return getResponse(new WebRequestSettings(url));
            }
            else {
                for (final Iterator frames = allFrames.entrySet().iterator(); frames.hasNext();) {
                  final Map.Entry frame = (Map.Entry) frames.next();
                    LOG.error(frame.getKey() + " -> " + ((URL) frame.getValue()).toExternalForm());
                }
                throw new StepFailedException("The applet didn't showDocument in " + fTarget);
            }
        }
        return null;
    }

    private AppletPluginResults readResults(AppletPluginArguments appletPluginArguments) {
        return (AppletPluginResults) FileUtil.tryReadObjectFromFile(appletPluginArguments.getOutputFile(), this);
    }

    private AppletPluginArguments setUpAppletPluginArguments() throws XPathExpressionException {
        final HtmlPage currentResponse = getContext().getCurrentHtmlResponse(this);
        final HtmlElement appletNode = (HtmlElement) getContext().getXPathHelper().selectFirst(currentResponse, getXpath());
        if (appletNode == null) {
            throw new StepFailedException("The specified element <" + getXpath() + "> was not found.", this);
        }

        final AppletPluginArguments appletPluginArguments = createAppletPluginArguments();
        appletPluginArguments.setAppletTag(extractAppletParameter(currentResponse.getWebResponse().getRequestUrl(), appletNode));
        return appletPluginArguments;
    }

    AppletPluginArguments createAppletPluginArguments() {
        final WebtestTask webtest = getContext().getWebtest();
        final AppletPluginArguments appletPluginArguments = new AppletPluginArguments();

        final StringBuffer sb = new StringBuffer(webtest.getName());
        sb.append(getDescription(" - ", ""));
        appletPluginArguments.setBaseWindowName(sb.toString());

        appletPluginArguments.setSaveResponse(webtest.getConfig().isSaveResponse());
        appletPluginArguments.setSaveDirectory(webtest.getConfig().getWebTestResultDir());
        appletPluginArguments.setOutputFile(createTempFile(".output"));
        appletPluginArguments.setScenarioLocation(convertPathToURL(fScenarioLocationPath));
        appletPluginArguments.setScenario(fScenario);
        for (Iterator iterator = getParameters(); iterator.hasNext();) {
            appletPluginArguments.addArgument((Parameter) iterator.next());
        }
        appletPluginArguments.addCookies(StoreCookie.getCookies(getContext()));
        return appletPluginArguments;
    }

    protected URL[] convertPathToURL(Path path) {
        if (path == null) {
            return AppletPluginArguments.EMPTY_URL_LIST;
        }
        String pathElements[] = path.list();
        final URL[] urls = new URL[pathElements.length];
        for (int i = 0; i < pathElements.length; i++) {
            urls[i] = UrlBoundary.tryCreateUrlFromFileWithError(new File(pathElements[i]), this);
        }
        return urls;
    }

    AbstractAppletTag extractAppletParameter(final URL url, final HtmlElement appletNode) {
        try {
            return AbstractAppletTag.newInstance(url, appletNode);
        } catch (Exception e) {
            throw new StepFailedException(e.getMessage(), this);
        }
    }

    private AppletPluginResults runApplet(AppletPluginArguments appletPluginArguments) {
        int exitValue = executeAsForked(appletPluginArguments);
        LOG.info("runApplet: exitValue was: " + exitValue);
        if (exitValue != 0) {
            final StringBuffer msg = new StringBuffer();
            msg.append("Test ").append(fScenario).append(" failed. Exit value: ").append(exitValue);

            final String s = msg.toString();
            LOG.error(s);
            throw new StepExecutionException(s, this);
        }
        AppletPluginResults apr = readResults(appletPluginArguments);

        verifyAppletResult(apr);

        for (Iterator properties = apr.getProperties().iterator(); properties.hasNext();) {
            AppletPluginResults.Property property = (AppletPluginResults.Property) properties.next();
            setWebtestProperty(property.getName(), property.getValue(), property.getType());
        }

        return apr;
    }

    void verifyAppletResult(AppletPluginResults apr) {
        if (apr.getException() != null) {
            throw new StepFailedException(apr.getException(), this);
        }
        if (apr.getJemmyException() != null) {
            throw new StepFailedException(apr.getJemmyException(), this);
        }
        if (!ZERO.equals(apr.getReturnValue())) {
            throw new StepFailedException("Scenario returns " + apr.getReturnValue(), this);
        }
    }

    /**
     * Execute a testcase by forking a new JVM. The command will block until it finishes.
     *
     * @param appletPluginArguments
     */
    private int executeAsForked(final AppletPluginArguments appletPluginArguments) {
        CommandlineJava cmd = AppletRunnerStepBoundary.tryClone(fCommandline, this);
        cmd.setClassname(AppletRunner.class.getName());
        cmd.addSysproperty(getProtocolHandler());
        cmd.createArgument().setValue(writeArguments(appletPluginArguments));
        Execute execute = new Execute(new LogStreamHandler(this, Project.MSG_INFO, Project.MSG_WARN));
        execute.setCommandline(cmd.getCommandline());
        execute.setAntRun(getProject());

        // propagate the environment here?
        LOG.info(cmd.describeCommand());
        return AppletRunnerStepBoundary.tryExecute(execute, this);
    }

    static Environment.Variable getProtocolHandler() {
        final Environment.Variable sysp = new Environment.Variable();
        sysp.setKey(PROTOCOL_HANDLER_LIST);
        final String handlers = System.getProperty(PROTOCOL_HANDLER_LIST, "");
        sysp.setValue(RUNNER_PACKAGE + (handlers.length() == 0 ? "" : "|" + handlers));
        return sysp;
    }

    /**
     * Adds all the jar required to start the appletrunner in a separate vm.
     *
     * @param cmd
     */
    private void setupClasspath(CommandlineJava cmd) {
        final Path classpath = cmd.createClasspath(getProject());
        appendToClasspath(LogFactory.class, classpath);
        appendToClasspath(HostConfiguration.class, classpath);
        appendToClasspath(Logger.class, classpath);
        appendToClasspath(Commandline.Argument.class, classpath);
        appendToClasspath(HtmlElement.class, classpath);
        appendToClasspath(DecoderException.class, classpath);
        appendOptionalToClasspath(A_CLOVER_CLASS, classpath);
        appendToClasspath(Scenario.class, classpath);
        appendToClasspath(AppletRunner.class, classpath);
        appendToClasspath(TestCase.class, classpath);
    }

    /**
     * Settings for log4j in the child jvm. The logic here is derived from the description in log4j's short manual.
     *
     * @param cmd The command line for the child jvm.
     */
    static void setupLog4j(CommandlineJava cmd) {
        String log4jDefaultInitOverride = System.getProperty(LOG4J_DEFAULT_INIT_OVERRIDES, "false");
        if ("false".equals(log4jDefaultInitOverride)) {
            String resource = System.getProperty(LOG4J_CONFIGURATION, "log4j.properties");
            URL resourceURL;
            try {
                resourceURL = new URL(resource);
            } catch (MalformedURLException e) {
                resourceURL = org.apache.log4j.helpers.Loader.getResource(resource);
            }
            if (resourceURL == null) {
                log4jDefaultInitOverride = "true";
            } else {
                setSysProperty(cmd, LOG4J_CONFIGURATION, resourceURL.toExternalForm());
            }
        }
        setSysProperty(cmd, LOG4J_DEFAULT_INIT_OVERRIDES, log4jDefaultInitOverride);
    }

    static void setSysProperty(final CommandlineJava cmd, final String key, final String value) {
        final Environment.Variable sysp = new Environment.Variable();
        sysp.setKey(key);
        sysp.setValue(value);
        cmd.addSysproperty(sysp);
    }

    /**
     * Adds a jar file for a given class to the classpath.
     *
     * @param aClass    The class to look for.
     * @param classpath The claspath to change.
     */
    void appendToClasspath(Class aClass, final Path classpath) {
        if (!appendOptionalToClasspath(aClass.getName(), classpath)) {
            String msg = "Can't locate required class " + aClass.getName();
            LOG.error(msg);
            throw new StepFailedException(msg, this);
        }
    }

    /**
     * Adds a jar file for a given class to the classpath.
     *
     * @param aClassName The class to look for.
     * @param classpath  The claspath to change.
     * @return True if the desired class was added to the classpath.
     */
    boolean appendOptionalToClasspath(String aClassName, final Path classpath) {
        URL url = AppletRunnerStepBoundary.tryGetUrlForClass(aClassName, this);
        if (url == null) {
            LOG.warn("Can't locate optional class " + aClassName);
            return false;
        }
        classpath.createPathElement().setLocation(new File(url.getFile()));
        return true;
    }

    private String writeArguments(AppletPluginArguments apa) {
        final File tmpFile = createTempFile(".arguments");
        FileUtil.tryWriteObjectToFile(tmpFile, apa, this);
        return tmpFile.getAbsolutePath();
    }

    private File createTempFile(final String suffix) {
        return FileUtil.tryCreateTempFile("AppletPlugin", suffix, this);
    }

    public static URL getUrlForClass(final String className) throws MalformedURLException {
        LOG.debug("Looking for " + className);
        final String classRsrc = className.replace('.', '/') + ".class";
        final URL resource = AppletRunnerStep.class.getClassLoader().getResource(classRsrc);
        if (resource == null) {
            return null;
        }
        return new URL(resource, extractClasspathEntry(resource.getFile(), classRsrc));
    }

    /**
     * Cuts off the name of the class and the '!/' that indicates a url into a jar.
     *
     * @param url
     * @param classRsrc
     * @return the class name
     */
    static String extractClasspathEntry(final String url, final String classRsrc) {
        int length = url.length() - classRsrc.length();
        if ("!/".equals(url.substring(length - 2, length))) {
            length -= 2;
        }
        return url.substring(0, length);
    }

    /**
     * Adds a JVM argument.
     *
     * @return create a new JVM argument so that any argument can be passed to the JVM.
     * @since Ant 1.2
     */
    public Commandline.Argument createJvmarg() {
        return fCommandline.createVmArgument();
  }
}
TOP

Related Classes of com.canoo.webtest.extension.applet.AppletRunnerStep

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.