edu.umd.cs.guitar.replayer.Replayer.java Source code

Java tutorial

Introduction

Here is the source code for edu.umd.cs.guitar.replayer.Replayer.java

Source

/*
 *  Copyright (c) 2009-@year@. The  GUITAR group  at the University of
 *  Maryland. Names of owners of this group may be obtained by sending
 *  an e-mail to atif@cs.umd.edu
 *
 *  Permission is hereby granted, free of charge, to any person obtaining
 *  a copy of this software and associated documentation files
 *  (the "Software"), to deal in the Software without restriction,
 *  including without limitation  the rights to use, copy, modify, merge,
 *  publish,  distribute, sublicense, and/or sell copies of the Software,
 *  and to  permit persons  to whom  the Software  is furnished to do so,
 *  subject to the following conditions:
 *
 *  The above copyright notice and this permission notice shall be included
 *  in all copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 *  OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 *  MERCHANTABILITY,  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 *  IN NO  EVENT SHALL THE  AUTHORS OR COPYRIGHT  HOLDERS BE LIABLE FOR ANY
 *  CLAIM, DAMAGES OR  OTHER LIABILITY,  WHETHER IN AN  ACTION OF CONTRACT,
 *  TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 *  SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
package edu.umd.cs.guitar.replayer;

import edu.umd.cs.guitar.event.GEvent;
import edu.umd.cs.guitar.exception.ComponentDisabled;
import edu.umd.cs.guitar.exception.ComponentNotFound;
import edu.umd.cs.guitar.exception.GException;
import edu.umd.cs.guitar.exception.ReplayerStateException;
import edu.umd.cs.guitar.model.GComponent;
import edu.umd.cs.guitar.model.GUITARConstants;
import edu.umd.cs.guitar.model.GWindow;
import edu.umd.cs.guitar.model.IO;
import edu.umd.cs.guitar.model.data.AttributesType;
import edu.umd.cs.guitar.model.data.EFG;
import edu.umd.cs.guitar.model.data.EventType;
import edu.umd.cs.guitar.model.data.GUIStructure;
import edu.umd.cs.guitar.model.data.PropertyType;
import edu.umd.cs.guitar.model.data.StepType;
import edu.umd.cs.guitar.model.data.TestCase;
import edu.umd.cs.guitar.model.wrapper.ComponentTypeWrapper;
import edu.umd.cs.guitar.model.wrapper.GUIStructureWrapper;
import edu.umd.cs.guitar.model.wrapper.GUITypeWrapper;
import edu.umd.cs.guitar.model.wrapper.PropertyTypeWrapper;
import edu.umd.cs.guitar.replayer.monitor.GTestMonitor;
import edu.umd.cs.guitar.replayer.monitor.GTestStepEventArgs;
import edu.umd.cs.guitar.replayer.monitor.TestStepEndEventArgs;
import edu.umd.cs.guitar.replayer.monitor.TestStepStartEventArgs;
import edu.umd.cs.guitar.util.GUITARLog;
import org.apache.commons.io.FileUtils;
import org.apache.log4j.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;

/**
 * Main replayer class, monitoring the replayer's behaviors
 * <p/>
 * Note on exception handling:
 * <p/>
 * GUITAR related exceptions MUST be derived from GExceptions. All non
 * GException exceptions are to be considered as AUT exceptions (unless
 * explicitly stated and handled, in an itemised manner).
 * <p/>
 * All "caught" exceptions MUST be propagated upwards unless expplicitly
 * itemised.
 * <p/>
 * <p/>
 *
 * @author <a href="mailto:baonn@cs.umd.edu"> Bao Nguyen </a>
 */
public class Replayer {
    /**
     * SECTION: DATA
     *
     * This section contains member variables and accessor functions.
     */

    /**
     * Test case data
     */
    TestCase tc;
    String sGUIFfile;
    String sEFGFfile;

    // Test Monitor
    GReplayerMonitor monitor;
    List<GTestMonitor> lTestMonitor = new ArrayList<GTestMonitor>();

    // Log
    Logger log = GUITARLog.log;

    // Secondary input
    GUIStructureWrapper guiStructureAdapter;
    EFG efg;
    Document docGUI;

    /**
     * Object-based Constructor
     */

    public Replayer(TestCase tc, GUIStructure guiStructure, EFG efgVal)
            throws ParserConfigurationException, IOException, SAXException {
        super();
        this.tc = tc;
        guiStructureAdapter = new GUIStructureWrapper(guiStructure);
        this.efg = efgVal;
        sDataPath = null;
        useImage = false;

        GUITARLog.log.info("GUI count is: " + guiStructure.getGUI().size());

        // Need to parse GUI Structure
        // Write GUIStructure to temp file
        File tdir = new File("tmpdir");
        FileUtils.deleteDirectory(tdir);

        if (!tdir.mkdir()) {
            GUITARLog.log.error("Could not make temp dir for gui xml parsing " + "files");
        }

        File tempFile = File.createTempFile("temp_gui", ".dat", tdir);

        IO.writeObjToFile(guiStructure, tempFile.getAbsolutePath());

        GUITARLog.log.info("Temp file created at: " + tempFile.getAbsolutePath());

        DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
        domFactory.setNamespaceAware(true);
        DocumentBuilder builder = domFactory.newDocumentBuilder();

        // Parse and store
        docGUI = builder.parse(tempFile.getAbsolutePath());
        GUITARLog.log.info(docGUI.getDocumentElement() == null);
    }

    /**
     * @param tc
     * @param sGUIFile
     * @param sEFGFile
     * @throws ParserConfigurationException
     * @throws IOException
     * @throws SAXException
     */
    public Replayer(TestCase tc, String sGUIFile, String sEFGFile)
            throws ParserConfigurationException, SAXException, IOException {
        super();
        this.tc = tc;
        this.sGUIFfile = sGUIFile;
        this.sEFGFfile = sEFGFile;

        // Initialize GUI object
        GUIStructure gui = (GUIStructure) IO.readObjFromFile(sGUIFile, GUIStructure.class);
        guiStructureAdapter = new GUIStructureWrapper(gui);

        // Initialize EFG object
        this.efg = (EFG) IO.readObjFromFile(sEFGFile, EFG.class);

        // Initialize Document gui object
        DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
        domFactory.setNamespaceAware(true);
        DocumentBuilder builder;
        builder = domFactory.newDocumentBuilder();
        docGUI = builder.parse(sGUIFile);

        // Initialize to null / disabled
        sDataPath = null;
        useImage = false;
    }

    public Replayer(String testcaseFile, String GUIFile, String EFGFile)
            throws ParserConfigurationException, SAXException, IOException {
        this((TestCase) IO.readObjFromFile(testcaseFile, TestCase.class), GUIFile, EFGFile);
    }

    /**
     * Time out for the replayer TODO: Move to a monitor
     */
    private int TIME_OUT = 0;

    /**
     * @param nTimeOut The nTimeOut to set
     */
    public void setTimeOut(int nTimeOut) {
        this.TIME_OUT = nTimeOut;
    }

    /**
     * Path for storing artifacts
     */
    String sDataPath;

    /**
     * Set the path where the replayer can find artifacts.
     *
     * @param sDataPath Name of path where the replayer finds artifacts
     */
    public void setDataPath(String sDataPath) {
        this.sDataPath = sDataPath;
    }

    /**
     * Use image based identification when possible.
     */
    boolean useImage = false;

    /**
     * useImage accessor
     */
    public void setUseImage() {
        this.useImage = true;
    }

    /**
     * SECTION: LOGIC
     *
     * This section contains the core logic for replaying a GUITAR testcase.
     */

    /**
     * Parse and run test case.
     *
     * @throws GException
     */
    public void execute() throws GException, Exception {
        log.info("------ BEGIN TESTCASE -----");

        try {
            monitor.setUp();

            log.info("Connecting to application");
            monitor.connectToApplication();
            log.info("Application is connected.");

            // Monitor before the test case
            for (GTestMonitor testMonitor : lTestMonitor) {
                GUITARLog.log
                        .info("Test monitor: " + testMonitor.getClass().getCanonicalName() + " is initialized");
                testMonitor.init();
            }

            log.info("Testcase size " + tc.getStep().size() + " steps");

            List<StepType> lSteps = tc.getStep();
            int nStep = lSteps.size();

            for (int i = 0; i < nStep; i++) {
                log.info("------- BEGIN STEP --------");
                StepType step = lSteps.get(i);
                executeStep(step);
                log.info("-------- END STEP ---------");
            }
            // Monitor after the test case
            for (GTestMonitor testMonitor : lTestMonitor) {
                testMonitor.term();
            }
            monitor.cleanUp();

        } catch (GException e) {
            for (GTestMonitor testMonitor : lTestMonitor) {
                testMonitor.exceptionHandler(e);
            }

            // Propagate error upwards

            log.info("-------- END STEP ---------");
            log.info("------- END TESTCASE ------");
            throw e;

        } catch (Exception e) {
            // Propagate error upwards

            log.info("-------- END STEP ---------");
            log.info("------- END TESTCASE ------");
            throw e;

        }

        log.info("------- END TESTCASE ------");
    }

    /**
     * Execute a single step in the test case
     * <p/>
     * <p/>
     * <p/>
     * TODO: Rewrite the test monitor
     *
     * @param step
     * @throws ComponentNotFound
     * @throws ReplayerStateException
     */
    void executeStep(StepType step) throws ComponentNotFound, ReplayerStateException, Exception {
        GTestStepEventArgs stepStartArgs = new TestStepStartEventArgs(step);

        // -----------------------
        // Monitor before step
        for (GTestMonitor aTestMonitor : lTestMonitor) {
            aTestMonitor.beforeStep(stepStartArgs);
        }

        // Events
        String sEventID = step.getEventId();
        GUITARLog.log.info("Executing Step EventID = " + sEventID);

        // Get widget ID and actions
        String sWidgetID = null;
        String sAction = null;

        List<EventType> lEvents = efg.getEvents().getEvent();

        for (EventType event : lEvents) {
            String eventID = event.getEventId();
            if (sEventID.equals(eventID)) {
                sWidgetID = event.getWidgetId();
                sAction = event.getAction();
            }
        }

        // Locate step event in EFG
        if (sWidgetID == null) {
            GUITARLog.log.error("Step Event ID = " + sEventID + ". Not found in EFG.");
            throw new ReplayerStateException();
        } else if (sAction == null) {
            GUITARLog.log.error("Step Event ID = " + sEventID + ". Action not found in EFG.");
            GUITARLog.log.error("Action not found");
            throw new ReplayerStateException();
        }

        // Lookup window for widget/event
        String sWindowTitle = getWindowName(sWidgetID);
        log.info("sWidgetID: " + sWidgetID);
        if (sWindowTitle == null) {
            GUITARLog.log.error("Step Event ID = " + sEventID + ". Unable to locate window for widget");
            throw new ReplayerStateException();
        }

        // Wait for window to appear
        GUITARLog.log.info("Waiting for window:");
        GUITARLog.log.info(" + Window Title = " + sWindowTitle);
        GUITARLog.log.info(" + Widget ID    = " + sWidgetID);

        // Wait for expected window to appear
        GWindow gWindow = replayerWaitForWindow(sWindowTitle);

        if (gWindow == null) {
            GUITARLog.log.error("Expected window did not appear");
            throw new ComponentNotFound();
        }

        GUITARLog.log.info("FOUND window");
        GUITARLog.log.info(" + Window Title = " + gWindow.getTitle());
        GUITARLog.log.info("");

        ComponentTypeWrapper comp = guiStructureAdapter.getComponentFromID(sWidgetID);

        if (comp == null) {
            GUITARLog.log.error("Component not found in GUI state.");
            throw new ReplayerStateException();
        }

        GUITARLog.log.info("Searching for widget:");
        GUITARLog.log.info(" + Widget ID = " + sWidgetID);

        /*
           * Once the window has been identified, search the window (gWindow)
         * for the widget to click (sWidgetID).
         */
        GComponent gComponent = replayerSearchComponent(gWindow, sWidgetID, comp);

        // Matching widget was not found
        if (gComponent == null) {
            // Bail out with exception
            GUITARLog.log.error("gComponent == null. " + "ComponentNotFound exception.");
            throw new ComponentNotFound();
        }

        // Matching widget was found
        GUITARLog.log.info("FOUND widget");
        GUITARLog.log.info(" + Widget Title = " + gComponent.getTitle());
        if (!gComponent.isEnable()) {
            GUITARLog.log.error(gComponent.getTitle() + " is disabled. " + "ComponentDisabled exception.");
            throw new ComponentDisabled();
        }

        // Execute action on matchd widget
        GEvent gEvent = monitor.getAction(sAction);
        List<String> parameters = step.getParameter();

        GUITARLog.log.info(" + Action: *" + sAction);
        GUITARLog.log.info("");

        // Optional data
        AttributesType optional = comp.getDComponentType().getOptional();
        Hashtable<String, List<String>> optionalValues = null;

        if (optional != null) {

            optionalValues = new Hashtable<String, List<String>>();
            for (PropertyType property : optional.getProperty()) {
                optionalValues.put(property.getName(), property.getValue());
            }
        }

        if (parameters == null) {
            gEvent.perform(gComponent, optionalValues);
        } else if (parameters.size() == 0) {
            gEvent.perform(gComponent, optionalValues);
        } else {
            gEvent.perform(gComponent, parameters, optionalValues);
        }

        // -----------------------
        // Monitor after step
        if (!lTestMonitor.isEmpty()) {
            try {
                GTestStepEventArgs stepEndArgs = new TestStepEndEventArgs(step, gComponent.extractProperties(),
                        gWindow.extractGUIProperties());
                for (GTestMonitor aTestMonitor : lTestMonitor) {
                    aTestMonitor.afterStep(stepEndArgs);
                }
            } catch (Exception e) {
                log.error("Failed to collect post-event state", e);

                // Propagate error upwards
                throw e;
            }
        }
    }

    /**
     * Get container window corresponding to a given widget ID.
     * This function looks up the GUI structure to extract the
     * window title of the window containing the widget.
     *
     * @param sWidgetID Widget ID for which container window is required
     * @return String    Window title of window containing sWidgetID
     */
    String getWindowName(String sWidgetID) {
        String sWindowName = null;

        XPath xpath = XPathFactory.newInstance().newXPath();
        XPathExpression expr;
        Object result;
        NodeList nodes;

        try {
            String xpathExpression = "/GUIStructure/GUI[Container//Property[Name=\"" + GUITARConstants.ID_TAG_NAME
                    + "\" and Value=\"" + sWidgetID + "\"]]/Window/Attributes/Property[Name=\""
                    + GUITARConstants.TITLE_TAG_NAME + "\"]/Value/text()";

            GUITARLog.log.info("The xpath is " + xpathExpression);

            expr = xpath.compile(xpathExpression);
            result = expr.evaluate(docGUI, XPathConstants.NODESET);
            nodes = (NodeList) result;

            GUITARLog.log.info("There are " + nodes.getLength() + " matching " + "nodes");

            if (nodes.getLength() > 0) {
                sWindowName = nodes.item(0).getNodeValue();
            }

        } catch (XPathExpressionException e) {
            /*
             * Not propagating. Return value is set to NULL instead. Caller
            * must
            * check.
            */
            GUITARLog.log.error("Error in XPath Expression", e);
        }
        log.info("Returning window name of " + sWindowName);
        return sWindowName;
    }

    /**
     * Get the replayer monitor
     *
     * @return the replayer monitor
     */
    public GReplayerMonitor getMonitor() {
        return monitor;
    }

    /**
     * @param monitor the replayer monitor to set
     */
    public void setMonitor(GReplayerMonitor monitor) {
        this.monitor = monitor;
    }

    /**
     * Add a test monitor
     * <p/>
     * <p/>
     *
     * @param aTestMonitor
     */
    public void addTestMonitor(GTestMonitor aTestMonitor) {
        aTestMonitor.setReplayer(this);
        this.lTestMonitor.add(aTestMonitor);
    }

    /**
     * Remove a test monitor
     * <p/>
     * <p/>
     *
     * @param mTestMonitor
     */
    public void removeTestMonitor(GTestMonitor mTestMonitor) {
        this.lTestMonitor.remove(mTestMonitor);
    }

    /**
     * Wait for a window to appear. This is a blocking call.
     * <p/>
     * Two methods of waiting for the new window are used.
     * *  useImage - use image based identification
     * * !useImage - use "window title" based identification
     *
     * @param sWindowTitle Window title of window to wait for
     */
    protected GWindow replayerWaitForWindow(String sWindowTitle) throws IOException {
        /*
         * This is a blocking call. Waits until window appears. Uses a regex
        * based match if specified in the command line.
        */
        GWindow gWindowByImage = null;
        GWindow gWindowByTitle = null;

        if (useImage) {
            String strUUID = null;

            // Find GUI Window image for comparing
            GUITypeWrapper guiTypeWrapperParent = guiStructureAdapter.getGUIByTitle(sWindowTitle);
            PropertyType propertyType = (guiTypeWrapperParent != null)
                    ? guiTypeWrapperParent.getWindowProperty(GUITARConstants.UUID_TAG_NAME)
                    : null;
            if (guiTypeWrapperParent != null) {
                strUUID = propertyType.getValue().get(0);

                gWindowByImage = monitor.waitForWindow(sWindowTitle, sDataPath + "/" + strUUID + ".png");
            }

            // Write ripped and replay image pair to log
            if (gWindowByImage != null) {
                monitor.writeMatchedComponents(gWindowByImage.getContainer(),
                        sDataPath + File.separatorChar + strUUID + ".png");
            }
        }

        // Always determine using the title based method
        gWindowByTitle = monitor.getWindow(sWindowTitle);

        // Determine if the two methods differed
        if (gWindowByTitle != null && gWindowByImage != null) {
            if (gWindowByTitle.hashCode() != gWindowByImage.hashCode()) {
                GUITARLog.log.info("Window identification mismatch");
                GUITARLog.log.info(
                        "Image based " + gWindowByImage.getTitle() + "Title based " + gWindowByTitle.getTitle());
            }
        }
        if (useImage && gWindowByTitle != null && gWindowByImage == null) {
            GUITARLog.log.info("Window identification mismatch");
            GUITARLog.log.info("Image based is null. " + "Title based " + gWindowByTitle.getTitle());
        }
        if (useImage && gWindowByTitle == null && gWindowByImage != null) {
            GUITARLog.log.info("Window identification mismatch");
            GUITARLog.log.info("Image based " + gWindowByImage.getTitle() + "Title based is null");
        }

        return useImage ? gWindowByImage : gWindowByTitle;
    }

    /**
     * Locate the component corresponding to sWidgetID inside gwindow.
     *
     * @param gWindow              Window in which to look for component
     * @param sWidgetID            Widget ID of widget to look for
     * @param componentTypeWrapper Component corresponding to sWidgetID
     * @returns GComponent of located window
     */
    GComponent replayerSearchComponent(GWindow gWindow, String sWidgetID, ComponentTypeWrapper componentTypeWrapper)
            throws IOException {
        GComponent container = gWindow.getContainer();

        GComponent gComponentByImage = null;
        GComponent gComponentByProp = null;

        if (useImage) {
            String strUUID = null;

            // Find GUI Window image for comparing
            ComponentTypeWrapper compTypeWrapper = guiStructureAdapter.getComponentFromID(sWidgetID);
            if (compTypeWrapper != null) {
                strUUID = (compTypeWrapper != null)
                        ? compTypeWrapper.getFirstValueByName(GUITARConstants.UUID_TAG_NAME)
                        : null;

                // baonn says: Because we decided not to put the image
                // feature directly in
                // the core, I commented it out so the Replayer is still
                // compatible with other
                // components
                //
                //            try {
                //                gComponentByImage =
                //                  container.searchComponentByImage(sDataPath +
                //                                                   "/" + strUUID + "
                // .png");
                //            } catch (IOException e) {
                //               throw new ReplayerStateException();
                //            }
            }

            // Write ripped and replay image pair to log
            if (gComponentByImage != null) {
                monitor.writeMatchedComponents(gComponentByImage,
                        sDataPath + File.separatorChar + strUUID + ".png");
            }
        }

        // Always perform property based identification
        List<PropertyType> ID = monitor.selectIDProperties(componentTypeWrapper.getDComponentType());
        List<PropertyTypeWrapper> IDAdapter = new ArrayList<PropertyTypeWrapper>();
        for (PropertyType p : ID) {
            IDAdapter.add(new PropertyTypeWrapper(p));
        }
        gComponentByProp = container.getFirstChild(IDAdapter);

        // Determine if the two methods were differed
        if (gComponentByProp != null && gComponentByImage != null) {
            if (gComponentByProp.hashCode() != gComponentByImage.hashCode()) {
                GUITARLog.log.info("Component identification mismatch");
                GUITARLog.log.info("Image based " + gComponentByImage.getTitle() + " Prop based "
                        + gComponentByProp.getTitle());
            }
        }
        if (useImage && gComponentByProp != null && gComponentByImage == null) {
            GUITARLog.log.info("Component identification mismatch");
            GUITARLog.log.info("Image based is null. " + " Prop based " + gComponentByProp.getTitle());
        }
        if (useImage && gComponentByProp == null && gComponentByImage != null) {
            GUITARLog.log.info("Component identification mismatch");
            GUITARLog.log.info("Image based " + gComponentByImage.getTitle() + " Prop based is null");
        }

        return useImage ? gComponentByImage : gComponentByProp;
    }

} // End of class