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

hudson.tasks.test.TestObject Maven / Gradle / Ivy

The newest version!
/*******************************************************************************
 *
 * Copyright (c) 2004-2010 Oracle Corporation.
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *
 *    Kohsuke Kawaguchi,   Tom Huybrechts, Yahoo!, Inc.
 *
 *
 *******************************************************************************/ 

package hudson.tasks.test;

import hudson.Util;
import hudson.Functions;
import hudson.model.*;
import hudson.tasks.junit.History;
import hudson.tasks.junit.TestAction;
import hudson.tasks.junit.TestResultAction;
import org.kohsuke.stapler.*;
import org.kohsuke.stapler.export.ExportedBean;

import com.google.common.collect.MapMaker;

import javax.servlet.ServletException;
import java.io.IOException;
import java.util.*;
import java.util.logging.Logger;

/**
 * Base class for all test result objects. For compatibility with code that
 * expects this class to be in hudson.tasks.junit, we've created a pure-abstract
 * class, hudson.tasks.junit.TestObject. That stub class is deprecated; instead,
 * people should use this class.
 *
 * @author Kohsuke Kawaguchi
 */
@ExportedBean
public abstract class TestObject extends hudson.tasks.junit.TestObject {

    private static final Logger LOGGER = Logger.getLogger(TestObject.class.getName());
    private volatile transient String id;

    public abstract AbstractBuild getOwner();

    /**
     * Reverse pointer of {@link TabulatedResult#getChildren()}.
     */
    public abstract TestObject getParent();

    @Override
    public final String getId() {
        if (id == null) {
            StringBuilder buf = new StringBuilder();
            buf.append(getSafeName());

            TestObject parent = getParent();
            if (parent != null) {
                String parentId = parent.getId();
                if ((parentId != null) && (parentId.length() > 0)) {
                    buf.insert(0, '/');
                    buf.insert(0, parent.getId());
                }
            }
            id = buf.toString();
        }
        return id;
    }

    /**
     * Returns url relative to TestResult
     */
    @Override
    public String getUrl() {
        return '/' + getId();
    }

    /**
     * Returns the top level test result data.
     *
     * @deprecated This method returns a JUnit specific class. Use
     * {@link #getTopLevelTestResult()} instead for a more general interface.
     * @return
     */
    @Override
    public hudson.tasks.junit.TestResult getTestResult() {
        TestObject parent = getParent();

        return (parent == null ? null : getParent().getTestResult());
    }

    /**
     * Returns the top level test result data.
     *
     * @return
     */
    public TestResult getTopLevelTestResult() {
        TestObject parent = getParent();

        return (parent == null ? null : getParent().getTopLevelTestResult());
    }

    /**
     * Computes the relative path to get to this test object from
     * it. If
     * it does not appear in the parent chain for this object, a
     * relative path from the server root will be returned.
     *
     * @return A relative path to this object, potentially from the top of the
     * Hudson object model
     */
    public String getRelativePathFrom(TestObject it) {


        // if (it is one of my ancestors) {
        //    return a relative path from it
        // } else {
        //    return a complete path starting with "/"
        // }
        if (it == this) {
            return ".";
        }

        StringBuilder buf = new StringBuilder();
        TestObject next = this;
        TestObject cur = this;
        // Walk up my ancesotors from leaf to root, looking for "it"
        // and accumulating a relative url as I go
        while (next != null && it != next) {
            cur = next;
            buf.insert(0, '/');
            buf.insert(0, cur.getSafeName());
            next = cur.getParent();
        }
        if (it == next) {
            return buf.toString();
        } else {
            // Keep adding on to the string we've built so far

            // Start with the test result action
            AbstractTestResultAction action = getTestResultAction();
            if (action == null) {
                LOGGER.warning("trying to get relative path, but we can't determine the action that owns this result.");
                return ""; // this won't take us to the right place, but it also won't 404.
            }
            buf.insert(0, '/');
            buf.insert(0, action.getUrlName());

            // Now the build
            AbstractBuild myBuild = cur.getOwner();
            if (myBuild == null) {
                LOGGER.warning("trying to get relative path, but we can't determine the build that owns this result.");
                return ""; // this won't take us to the right place, but it also won't 404. 
            }
            buf.insert(0, '/');
            buf.insert(0, myBuild.getUrl());

            // If we're inside a stapler request, just delegate to Hudson.Functions to get the relative path!
            StaplerRequest req = Stapler.getCurrentRequest();
            if (req != null && myBuild instanceof Item) {
                buf.insert(0, '/');
                // Ugly but I don't see how else to convince the compiler that myBuild is an Item
                Item myBuildAsItem = (Item) myBuild;
                buf.insert(0, Functions.getRelativeLinkTo(myBuildAsItem));
            } else {
                // We're not in a stapler request. Okay, give up.
                LOGGER.info("trying to get relative path, but it is not my ancestor, and we're not in a stapler request. Trying absolute hudson url...");
                String hudsonRootUrl = Hudson.getInstance().getRootUrl();
                if (hudsonRootUrl == null || hudsonRootUrl.length() == 0) {
                    LOGGER.warning("Can't find anything like a decent hudson url. Punting, returning empty string.");
                    return "";

                }
                buf.insert(0, '/');
                buf.insert(0, hudsonRootUrl);
            }

            LOGGER.info("Here's our relative path: " + buf.toString());
            return buf.toString();
        }

    }

    /**
     * Subclasses may override this method if they are associated with a
     * particular subclass of AbstractTestResultAction.
     *
     * @return the test result action that connects this test result to a
     * particular build
     */
    @Override
    public AbstractTestResultAction getTestResultAction() {
        AbstractBuild owner = getOwner();
        if (owner != null) {
            return owner.getAction(AbstractTestResultAction.class);
        } else {
            LOGGER.warning("owner is null when trying to getTestResultAction.");
            return null;
        }
    }

    /**
     * Get a list of all TestActions associated with this TestObject.
     *
     * @return
     */
    @Override
    public List getTestActions() {
        AbstractTestResultAction atra = getTestResultAction();
        if ((atra != null) && (atra instanceof TestResultAction)) {
            TestResultAction tra = (TestResultAction) atra;
            return tra.getActions(this);
        } else {
            return new ArrayList();
        }
    }

    /**
     * Gets a test action of the class passed in.
     *
     * @param klazz
     * @param  an instance of the class passed in
     * @return
     */
    @Override
    public  T getTestAction(Class klazz) {
        for (TestAction action : getTestActions()) {
            if (klazz.isAssignableFrom(action.getClass())) {
                return klazz.cast(action);
            }
        }
        return null;
    }

    /**
     * Gets the counterpart of this {@link TestResult} in the previous run.
     *
     * @return null if no such counter part exists.
     */
    public abstract TestResult getPreviousResult();

    /**
     * Gets the counterpart of this {@link TestResult} in the specified run.
     *
     * @return null if no such counter part exists.
     */
    public abstract TestResult getResultInBuild(AbstractBuild build);

    /**
     * Find the test result corresponding to the one identified by
     * id> withint this test result.
     *
     * @param id The path to the original test result
     * @return A corresponding test result, or null if there is no corresponding
     * result.
     */
    public abstract TestResult findCorrespondingResult(String id);

    /**
     * Time took to run this test. In seconds.
     */
    public abstract float getDuration();

    /**
     * Returns the string representation of the {@link #getDuration()}, in a
     * human readable format.
     */
    @Override
    public String getDurationString() {
        return Util.getTimeSpanString((long) (getDuration() * 1000));
    }

    @Override
    public String getDescription() {
        AbstractTestResultAction action = getTestResultAction();
        if (action != null) {
            return action.getDescription(this);
        }
        return "";
    }

    @Override
    public void setDescription(String description) {
        AbstractTestResultAction action = getTestResultAction();
        if (action != null) {
            action.setDescription(this, description);
        }
    }

    /**
     * Exposes this object through the remote API.
     */
    @Override
    public Api getApi() {
        return new Api(this);
    }

    /**
     * Gets the name of this object.
     */
    @Override
    public/* abstract */ String getName() {
        return "";
    }

    /**
     * Gets the version of {@link #getName()} that's URL-safe.
     */
    @Override
    public String getSafeName() {
        return safe(getName());
    }

    @Override
    public String getSearchUrl() {
        return getSafeName();
    }

    /**
     * #2988: uniquifies a {@link #getSafeName} amongst children of the parent.
     */
    protected final String uniquifyName(Collection siblings, String base) {
        synchronized (UNIQUIFIED_NAMES) {
            String uniquified = base;
            Set siblingsPrevUsed = UNIQUIFIED_NAMES.get(base);
            if (siblingsPrevUsed == null) {
                siblingsPrevUsed = Collections.newSetFromMap(new WeakHashMap());
                UNIQUIFIED_NAMES.put(base, siblingsPrevUsed);
            } else {
                Set tmpFilter = new HashSet(siblingsPrevUsed);
                tmpFilter.retainAll(new HashSet(siblings));
                if (!tmpFilter.isEmpty()) {
                    uniquified = base + '_' + (tmpFilter.size() + 1);
                }
            }
            siblingsPrevUsed.add(this);
            return uniquified;
        }
    }
    
    private static final Map> UNIQUIFIED_NAMES = new MapMaker().weakKeys().makeMap();

    /**
     * Replaces URL-unsafe characters.
     */
    public static String safe(String s) {
        // 3 replace calls is still 2-3x faster than a regex replaceAll
        return s.replace('/', '_').replace('\\', '_').replace(':', '_');
    }

    /**
     * Gets the total number of passed tests.
     */
    public abstract int getPassCount();

    /**
     * Gets the total number of failed tests.
     */
    public abstract int getFailCount();

    /**
     * Gets the total number of skipped tests.
     */
    public abstract int getSkipCount();

    /**
     * Gets the total number of tests.
     */
    @Override
    public int getTotalCount() {
        return getPassCount() + getFailCount() + getSkipCount();
    }

    @Override
    public History getHistory() {
        return new History(this);
    }

    public Object getDynamic(String token, StaplerRequest req,
            StaplerResponse rsp) {
        for (Action a : getTestActions()) {
            if (a == null) {
                continue; // be defensive
            }
            String urlName = a.getUrlName();
            if (urlName == null) {
                continue;
            }
            if (urlName.equals(token)) {
                return a;
            }
        }
        return null;
    }

    public synchronized HttpResponse doSubmitDescription(
            @QueryParameter String description) throws IOException,
            ServletException {
        if (getOwner() == null) {
            LOGGER.severe("getOwner() is null, can't save description.");
        } else {
            getOwner().checkPermission(Run.UPDATE);
            setDescription(description);
            getOwner().save();
        }

        return new HttpRedirect(".");
    }
    private static final long serialVersionUID = 1L;
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy