org.opens.tanaguru.sebuilder.interpreter.TgTestRun.java Source code

Java tutorial

Introduction

Here is the source code for org.opens.tanaguru.sebuilder.interpreter.TgTestRun.java

Source

/*
 *  Tanaguru - Automated webpage assessment
 *  Copyright (C) 2008-2013  Open-S Company
 * 
 *  This file is part of Tanaguru.
 * 
 *  Tanaguru is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 * 
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 * 
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 * 
 *  Contact us by mail: open-s AT open-s DOT com
 */
package org.opens.tanaguru.sebuilder.interpreter;

import com.sebuilder.interpreter.Script;
import com.sebuilder.interpreter.TestRun;
import com.sebuilder.interpreter.Verify;
import com.sebuilder.interpreter.webdriverfactory.WebDriverFactory;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.*;
import javax.imageio.ImageIO;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.openqa.selenium.*;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.remote.RemoteWebDriver;
import org.opens.tanaguru.sebuilder.interpreter.exception.TestRunException;
import org.opens.tanaguru.sebuilder.tools.FirefoxDriverObjectPool;

/**
 * A single run of a test getScript().
 *
 * @author jkowalczyk
 */
public class TgTestRun extends TestRun {

    private boolean isStepOpenNewPage = false;
    private Map<String, String> jsScriptMap;

    public Map<String, String> getJsScriptMap() {
        return jsScriptMap;
    }

    public void setJsScriptMap(Map<String, String> jsScriptMap) {
        this.jsScriptMap = jsScriptMap;
    }

    private Collection<NewPageListener> newPageListeners;

    public void addNewPageListener(NewPageListener newPageListener) {
        if (newPageListeners == null) {
            newPageListeners = new ArrayList<NewPageListener>();
        }
        this.newPageListeners.add(newPageListener);
    }

    public void addNewPageListeners(Collection<NewPageListener> newPageListeners) {
        if (this.newPageListeners == null) {
            this.newPageListeners = new ArrayList<NewPageListener>();
        }
        this.newPageListeners.addAll(newPageListeners);
    }

    public void removeNewPageListener(NewPageListener newPageListener) {
        if (newPageListeners != null) {
            this.newPageListeners.remove(newPageListener);
        }
    }

    public void removeNewPageListeners(Collection<NewPageListener> newPageListeners) {
        if (newPageListeners != null) {
            this.newPageListeners.removeAll(newPageListeners);
        }
    }

    private RemoteWebDriver rwd;

    @Override
    public RemoteWebDriver getDriver() {
        if (firefoxDriverObjectPool != null) {
            return rwd;
        } else {
            // keep the ability not to use an object pool
            return super.getDriver();
        }
    }

    private FirefoxDriverObjectPool firefoxDriverObjectPool;

    public void setFirefoxDriverObjectPool(FirefoxDriverObjectPool firefoxDriverObjectPool) {
        this.firefoxDriverObjectPool = firefoxDriverObjectPool;
    }

    /**
     * Constructor
     * @param script
     * @param implicitelyWaitDriverTimeout
     * @param pageLoadDriverTimeout 
     */
    public TgTestRun(Script script, int implicitelyWaitDriverTimeout, int pageLoadDriverTimeout) {
        super(script, implicitelyWaitDriverTimeout, pageLoadDriverTimeout);
    }

    /**
     * Constructor
     * @param script
     * @param log
     * @param webDriverFactory
     * @param webDriverConfig
     * @param implicitelyWaitDriverTimeout
     * @param pageLoadDriverTimeout 
     */
    public TgTestRun(Script script, Log log, WebDriverFactory webDriverFactory,
            HashMap<String, String> webDriverConfig, int implicitelyWaitDriverTimeout, int pageLoadDriverTimeout) {
        super(script, log, webDriverFactory, webDriverConfig, implicitelyWaitDriverTimeout, pageLoadDriverTimeout);
    }

    /**
     * Constructor
     * @param script
     * @param webDriverFactory
     * @param webDriverConfig
     * @param implicitelyWaitDriverTimeout
     * @param pageLoadDriverTimeout 
     */
    public TgTestRun(Script script, WebDriverFactory webDriverFactory, HashMap<String, String> webDriverConfig,
            int implicitelyWaitDriverTimeout, int pageLoadDriverTimeout) {
        super(script, webDriverFactory, webDriverConfig, implicitelyWaitDriverTimeout, pageLoadDriverTimeout);
    }

    /**
     * @return True if there is another step to execute.
     */
    @Override
    public boolean hasNext() {
        boolean hasNext = stepIndex < getScript().steps.size() - 1;
        if (!hasNext && getDriver() != null) {
            properlyCloseWebDriver();
        }
        return hasNext;
    }

    /**
     * Executes the next step.
     *
     * @return True on success.
     */
    @Override
    public boolean next() {
        if (stepIndex == -1) {
            getLog().debug("Starting test run.");
        }

        initRemoteWebDriver();

        isStepOpenNewPage = false;

        getLog().debug("Running step " + (stepIndex + 2) + ":"
                + getScript().steps.get(stepIndex + 1).type.toString() + " step.");

        boolean result = false;
        String previousUrl = null;
        try {
            previousUrl = getDriver().getCurrentUrl();
            result = getScript().steps.get(++stepIndex).type.run(this);
            // wait a second to make sure the page is fully loaded
            Thread.sleep(500);
            if (!isStepOpenNewPage && !StringUtils.equals(previousUrl, getDriver().getCurrentUrl())) {
                fireNewPage();
            }
        } catch (TimeoutException te) {
            result = true;
            if (!isStepOpenNewPage && !StringUtils.equals(previousUrl, getDriver().getCurrentUrl())) {
                getLog().debug(" The page " + getDriver().getCurrentUrl() + " is fired as new page but"
                        + " is incomplete : " + te.getMessage());
                fireNewPage();
            }
        } catch (UnhandledAlertException uae) {
            getLog().warn(uae.getMessage());
            properlyCloseWebDriver();
            throw new TestRunException(currentStep() + " failed.", uae, currentStep().toString(), stepIndex);
        } catch (Exception e) {
            getLog().warn(e.getCause());
            getLog().warn(e.getMessage());
            properlyCloseWebDriver();
            throw new TestRunException(currentStep() + " failed.", e, currentStep().toString(), stepIndex);
        }

        if (!result) {
            // If a verify failed, we just note this but continue.
            if (currentStep().type instanceof Verify) {
                getLog().error(currentStep() + " failed.");
                return false;
            }
            // In all other cases, we throw an exception to stop the run.
            RuntimeException e = new TestRunException(currentStep() + " failed.", currentStep().toString(),
                    stepIndex);
            e.fillInStackTrace();
            getLog().error(e);
            properlyCloseWebDriver();
            throw e;
        } else {
            return true;
        }
    }

    /**
     * 
     * @param urlSuffix 
     */
    public void fireNewPage(String urlSuffix) {
        isStepOpenNewPage = true;
        if (newPageListeners == null) {
            return;
        }
        getSourceCodeAndFireNewPage(getDriver().getCurrentUrl() + '#' + urlSuffix);
    }

    /**
     * 
     */
    public void fireNewPage() {
        isStepOpenNewPage = true;
        if (newPageListeners == null) {
            return;
        }
        getSourceCodeAndFireNewPage(getDriver().getCurrentUrl());
    }

    /**
     * 
     * @param url 
     */
    private void getSourceCodeAndFireNewPage(String url) {
        try {
            try {
                Thread.sleep(500);
            } catch (InterruptedException ex) {
                throw new TestRunException(currentStep() + " failed.", ex, currentStep().toString(), stepIndex);
            }
            String sourceCode = getDriver().getPageSource();

            /* ##############################################################
             * ACHTUNG !!!!!!!!!!!!!!!!!!!!!!!!!!
             * this sendKeys action is here to grab the focus on the page.
             * It is needed later by the js script to execute the focus()
             * method on each element. Without it, the focus is kept by the adress
             * bar.
             */
            WebElement body = getDriver().findElementByTagName("html");
            Map<String, String> jsScriptResult = Collections.EMPTY_MAP;
            try {
                body.sendKeys(Keys.TAB);
                jsScriptResult = executeJsScripts();
            } catch (WebDriverException wde) {
                getLog().warn(wde.getMessage());
            }
            /*##############################################################*/

            /* byte[] snapshot = createPageSnapshot();*/
            for (NewPageListener npl : newPageListeners) {
                npl.fireNewPage(url, sourceCode, null, jsScriptResult);
            }
        } catch (UnhandledAlertException uae) {
            getLog().warn(uae.getMessage());
            throw new TestRunException(currentStep() + " failed.", uae, currentStep().toString(), stepIndex);
        }
    }

    /**
     * 
     * @return 
     */
    private Map<String, String> executeJsScripts() {
        getLog().debug("Executing js");
        Map<String, String> jsScriptResult = new HashMap<String, String>();
        for (Map.Entry<String, String> entry : jsScriptMap.entrySet()) {
            try {
                jsScriptResult.put(entry.getKey(), getDriver().executeScript(entry.getValue()).toString());
            } catch (WebDriverException wde) {
                getLog().warn("Script " + entry.getKey() + " has failed");
                getLog().warn(wde.getMessage());
            }
        }
        getLog().debug("Js executed");
        return jsScriptResult;
    }

    /**
     * This methods uses the screenshot service of Webdriver to create the page
     * snapshot.
     * 
     * @return 
     */
    private byte[] createPageSnapshot() {
        if (!(getDriver() instanceof TakesScreenshot)) {
            return null;
        }
        byte[] snapshot = null;
        try {
            getLog().debug("Creating snapshot");
            snapshot = ((TakesScreenshot) getDriver()).getScreenshotAs(OutputType.BYTES);
            BufferedImage snapshotImg = ImageIO.read(new ByteArrayInputStream(snapshot));
            BufferedImage scaledImg = resizeImage(snapshotImg);
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ImageIO.write(scaledImg, "png", baos);
            baos.flush();
            snapshot = baos.toByteArray();
            baos.close();
        } catch (IOException ex) {
            getLog().error("Error when creating snapshot");
            getLog().error(ex);
        }
        getLog().error("Snapshot created");
        return snapshot;
    }

    /**
     * Resize the snapshot image to 270*170
     * 
     * @param originalImage
     * @return 
     */
    private BufferedImage resizeImage(BufferedImage originalImage) {
        float scaleRatio = (float) 270 / originalImage.getWidth();
        int resizedImageHeight = Float.valueOf(originalImage.getHeight() * scaleRatio).intValue();
        BufferedImage resizedImage = new BufferedImage(270, resizedImageHeight, originalImage.getType());
        Graphics2D g = resizedImage.createGraphics();
        g.drawImage(originalImage, 0, 0, 270, resizedImageHeight, null);
        g.dispose();

        return resizedImage.getSubimage(0, 0, 270, 170);
    }

    @Override
    public void initRemoteWebDriver() {
        if (firefoxDriverObjectPool != null && getDriver() == null) {
            getLog().debug("Initialisation FirefoxDriver terminated ");
            try {
                rwd = firefoxDriverObjectPool.borrowObject();
            } catch (Exception ex) {
                getLog().warn("Firefox driver cannot be borrowed due to  " + ex.getMessage());
            }
            // if the firefoxDriver object pool is not set, keep the default behaviour
        } else if (getDriver() == null) {
            getLog().debug("Launching initialisation of WebDriver");
            super.initRemoteWebDriver();
            getLog().debug("WebDriver initialised");
        }
    }

    /**
     * Properly Returns the WebDriver Object to the pool when finished or
     * close and quit the driver if the object pool is not used
     */
    private void properlyCloseWebDriver() {
        getLog().debug("Closing Firefox driver.");
        if (firefoxDriverObjectPool != null && getDriver() instanceof FirefoxDriver) {
            //set the blank page before returning the webDriver instance
            getDriver().get("");
            try {
                firefoxDriverObjectPool.returnObject((FirefoxDriver) getDriver());
            } catch (Exception ex) {
                getLog().warn("Firefox driver cannot be returned due to  " + ex.getMessage());
            }
        } else {
            try {
                getDriver().close();
                getDriver().quit();
            } catch (Exception e) {
                getLog().warn("An error occured while closing driver."
                        + " A defunct firefox process may run on the system. " + " Trying to kill before leaving");
                if (getDriver() instanceof FirefoxDriver) {
                    ((FirefoxDriver) getDriver()).kill();
                }
            }
        }
    }

}