<commit_before>/*
 * The MIT License
 * 
 * Copyright (c) 2004-2009, Sun Microsystems, Inc., <NAME>, <NAME>
 * 
 * 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 org.jvnet.hudson.test;

import com.gargoylesoftware.htmlunit.AjaxController;
import com.gargoylesoftware.htmlunit.BrowserVersion;
import com.gargoylesoftware.htmlunit.FailingHttpStatusCodeException;
import com.gargoylesoftware.htmlunit.Page;
import com.gargoylesoftware.htmlunit.WebRequestSettings;
import com.gargoylesoftware.htmlunit.html.HtmlButton;
import com.gargoylesoftware.htmlunit.html.HtmlForm;
import com.gargoylesoftware.htmlunit.html.HtmlPage;
import com.gargoylesoftware.htmlunit.javascript.JavaScriptEngine;
import com.gargoylesoftware.htmlunit.javascript.host.Stylesheet;
import com.gargoylesoftware.htmlunit.javascript.host.XMLHttpRequest;
import hudson.CloseProofOutputStream;
import hudson.FilePath;
import hudson.Functions;
import hudson.Launcher.LocalLauncher;
import hudson.WebAppMain;
import hudson.matrix.MatrixProject;
import hudson.maven.MavenModuleSet;
import hudson.maven.MavenReporters;
import hudson.model.Descriptor;
import hudson.model.FreeStyleProject;
import hudson.model.Hudson;
import hudson.model.Item;
import hudson.model.JDK;
import hudson.model.Label;
import hudson.model.Node.Mode;
import hudson.model.Result;
import hudson.model.Run;
import hudson.model.Saveable;
import hudson.model.TaskListener;
import hudson.model.UpdateCenter;
import hudson.slaves.CommandLauncher;
import hudson.slaves.DumbSlave;
import hudson.slaves.RetentionStrategy;
import hudson.tasks.BuildStep;
import hudson.tasks.Mailer;
import hudson.tasks.Maven;
import hudson.tasks.Maven.MavenInstallation;
import hudson.util.ProcessTreeKiller;
import hudson.util.StreamTaskListener;
import junit.framework.TestCase;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.FileUtils;
import org.jvnet.hudson.test.HudsonHomeLoader.CopyExisting;
import org.jvnet.hudson.test.recipes.Recipe;
import org.jvnet.hudson.test.recipes.Recipe.Runner;
import org.jvnet.hudson.test.rhino.JavaScriptDebugger;
import org.mortbay.jetty.Server;
import org.mortbay.jetty.bio.SocketConnector;
import org.mortbay.jetty.security.HashUserRealm;
import org.mortbay.jetty.security.UserRealm;
import org.mortbay.jetty.webapp.Configuration;
import org.mortbay.jetty.webapp.WebAppContext;
import org.mortbay.jetty.webapp.WebXmlConfiguration;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.ContextFactory;
import org.w3c.css.sac.CSSException;
import org.w3c.css.sac.CSSParseException;
import org.w3c.css.sac.ErrorHandler;
import org.xml.sax.SAXException;
import org.kohsuke.stapler.MetaClass;
import org.kohsuke.stapler.Dispatcher;
import org.kohsuke.stapler.MetaClassLoader;

import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.lang.annotation.Annotation;
import java.lang.ref.WeakReference;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.jar.Manifest;
import java.util.logging.Filter;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;

/**
 * Base class for all Hudson test cases.
 *
 * @see <a href="http://hudson.gotdns.com/wiki/display/HUDSON/Unit+Test">Wiki article about unit testing in Hudson</a>
 * @author <NAME>
 */
public abstract class HudsonTestCase extends TestCase {
    public Hudson hudson;

    protected final TestEnvironment env = new TestEnvironment();
    protected HudsonHomeLoader homeLoader = HudsonHomeLoader.NEW;
    /**
     * TCP/IP port that the server is listening on.
     */
    protected int localPort;
    protected Server server;

    /**
     * Where in the {@link Server} is Hudson deploed?
     */
    protected String contextPath = "/";

    /**
     * {@link Runnable}s to be invoked at {@link #tearDown()}.
     */
    protected List<LenientRunnable> tearDowns = new ArrayList<LenientRunnable>();

    protected List<Runner> recipes = new ArrayList<Runner>();

    /**
     * Remember {@link WebClient}s that are created, to release them properly.
     */
    private List<WeakReference<WebClient>> clients = new ArrayList<WeakReference<WebClient>>();

    /**
     * JavaScript "debugger" that provides you information about the JavaScript call stack
     * and the current values of the local variables in those stack frame.
     *
     * <p>
     * Unlike Java debugger, which you as a human interfaces directly and interactively,
     * this JavaScript debugger is to be interfaced by your program (or through the
     * expression evaluation capability of your Java debugger.) 
     */
    protected JavaScriptDebugger jsDebugger = new JavaScriptDebugger();

    protected HudsonTestCase(String name) {
        super(name);
    }

    protected HudsonTestCase() {
    }

    protected void setUp() throws Exception {
        env.pin();
        recipe();
        hudson = newHudson();
        hudson.setNoUsageStatistics(true); // collecting usage stats from tests are pointless.
        hudson.servletContext.setAttribute("app",hudson);
        hudson.servletContext.setAttribute("version","?");
        WebAppMain.installExpressionFactory(new ServletContextEvent(hudson.servletContext));

        // set a default JDK to be the one that the harness is using.
        hudson.getJDKs().add(new JDK("default",System.getProperty("java.home")));

        // cause all the descriptors to reload.
        // ideally we'd like to reset them to properly emulate the behavior, but that's not possible.
        Mailer.DESCRIPTOR.setHudsonUrl(null);
        for( Descriptor d : Descriptor.ALL )
            d.load();
    }

    protected void tearDown() throws Exception {
        // cancel pending asynchronous operations, although this doesn't really seem to be working
        for (WeakReference<WebClient> client : clients) {
            WebClient c = client.get();
            if(c==null) continue;
            // unload the page to cancel asynchronous operations 
            c.getPage("about:blank");
        }
        clients.clear();

        server.stop();
        for (LenientRunnable r : tearDowns)
            r.run();

        // TODO: avoid relying on singletons and switch to some DI container.
        // In the mean time, discard descriptors created during this exercise.
        // without this, plugins loaded in the tests will be left and interferes with the later tests.
        cleanUpDescriptors(Descriptor.ALL);
        cleanUpDescriptors(BuildStep.PUBLISHERS);
        cleanUpDescriptors(MavenReporters.LIST);

        hudson.cleanUp();
        env.dispose();
    }

    private void cleanUpDescriptors(Iterable<? extends Descriptor> cont) {
        ClassLoader base = getClass().getClassLoader();
        for (Iterator<? extends Descriptor> itr = cont.iterator(); itr.hasNext();) {
            Descriptor d =  itr.next();
            ClassLoader cl = d.getClass().getClassLoader();
            if(cl==base)    continue;

            while(cl!=null) {
                cl = cl.getParent();
                if(cl==base) {
                    itr.remove();
                    break;
                }
            }
        }
    }

    protected void runTest() throws Throwable {
        System.out.println("=== Starting "+getName());
        new JavaScriptEngine(null);   // ensure that ContextFactory is initialized
        Context cx= ContextFactory.getGlobal().enterContext();
        try {
            cx.setOptimizationLevel(-1);
            cx.setDebugger(jsDebugger,null);

            super.runTest();
        } finally {
            Context.exit();
        }
    }

    /**
     * Creates a new instance of {@link Hudson}. If the derived class wants to create it in a different way,
     * you can override it.
     */
    protected Hudson newHudson() throws Exception {
        File home = homeLoader.allocate();
        for (Runner r : recipes)
            r.decorateHome(this,home);
        return new Hudson(home, createWebServer());
    }

    /**
     * Prepares a webapp hosting environment to get {@link ServletContext} implementation
     * that we need for testing.
     */
    protected ServletContext createWebServer() throws Exception {
        server = new Server();

        WebAppContext context = new WebAppContext(WarExploder.getExplodedDir().getPath(), contextPath);
        context.setClassLoader(getClass().getClassLoader());
        context.setConfigurations(new Configuration[]{new WebXmlConfiguration(),new NoListenerConfiguration()});
        server.setHandler(context);

        SocketConnector connector = new SocketConnector();
        server.addConnector(connector);
        server.addUserRealm(configureUserRealm());
        server.start();

        localPort = connector.getLocalPort();

        return context.getServletContext();
    }

    /**
     * Configures a security realm for a test.
     */
    protected UserRealm configureUserRealm() {
        HashUserRealm realm = new HashUserRealm();
        realm.setName("default");   // this is the magic realm name to make it effective on everywhere
        realm.put("alice","alice");
        realm.put("bob","bob");
        realm.put("charlie","charlie");

        realm.addUserToRole("alice","female");
        realm.addUserToRole("bob","male");
        realm.addUserToRole("charlie","male");

        return realm;
    }

    /**
     * Locates Maven2 and configure that as the only Maven in the system.
     */
    protected void configureDefaultMaven() throws Exception {
        // first if we are running inside Maven, pick that Maven.
        String home = System.getProperty("maven.home");
        if(home!=null) {
            Maven.DESCRIPTOR.setInstallations(new MavenInstallation("default",home));
            return;
        }

        // otherwise extract the copy we have.
        // this happens when a test is invoked from an IDE, for example.
        LOGGER.warning("Extracting a copy of Maven bundled in the test harness. " +
                "To avoid a performance hit, set the system property 'maven.home' to point to a Maven2 installation.");
        FilePath mvn = hudson.getRootPath().createTempFile("maven", "zip");
        OutputStream os = mvn.write();
        try {
            IOUtils.copy(HudsonTestCase.class.getClassLoader().getResourceAsStream("maven-2.0.7-bin.zip"), os);
        } finally {
            os.close();
        }
        File mvnHome = createTmpDir();
        mvn.unzip(new FilePath(mvnHome));

        Maven.DESCRIPTOR.setInstallations(new MavenInstallation("default",
                new File(mvnHome,"maven-2.0.7").getAbsolutePath()));
    }

//
// Convenience methods
//

    protected FreeStyleProject createFreeStyleProject() throws IOException {
        return createFreeStyleProject(createUniqueProjectName());
    }

    protected FreeStyleProject createFreeStyleProject(String name) throws IOException {
        return (FreeStyleProject)hudson.createProject(FreeStyleProject.DESCRIPTOR,name);
    }

    protected MatrixProject createMatrixProject() throws IOException {
        return createMatrixProject(createUniqueProjectName());
    }

    protected MatrixProject createMatrixProject(String name) throws IOException {
        return (MatrixProject)hudson.createProject(MatrixProject.DESCRIPTOR,name);
    }

    /**
     * Creates a empty Maven project with an unique name.
     *
     * @see #configureDefaultMaven()
     */
    protected MavenModuleSet createMavenProject() throws IOException {
        return createMavenProject(createUniqueProjectName());
    }

    /**
     * Creates a empty Maven project with the given name.
     *
     * @see #configureDefaultMaven()
     */
    protected MavenModuleSet createMavenProject(String name) throws IOException {
        return (MavenModuleSet)hudson.createProject(MavenModuleSet.DESCRIPTOR,name);
    }

    private String createUniqueProjectName() {
        return "test"+hudson.getItems().size();
    }

    /**
     * Creates {@link LocalLauncher}. Useful for launching processes.
     */
    protected LocalLauncher createLocalLauncher() {
        return new LocalLauncher(new StreamTaskListener(System.out));
    }

    /**
     * Allocates a new temporary directory for the duration of this test.
     */
    public File createTmpDir() throws IOException {
        return env.temporaryDirectoryAllocator.allocate();
    }

    public DumbSlave createSlave() throws Exception {
        return createSlave(null);
    }

    /**
     * Creates and launches a new slave on the local host.
     */
    public DumbSlave createSlave(Label l) throws Exception {
        CommandLauncher launcher = new CommandLauncher(
                System.getProperty("java.home") + "/bin/java -jar " + hudson.getJnlpJars("slave.jar").getURL().getPath());

        // this synchronization block is so that we don't end up adding the same slave name more than once.
        synchronized (hudson) {
            DumbSlave slave = new DumbSlave("slave" + hudson.getNodes().size(), "dummy",
                    createTmpDir().getPath(), "1", Mode.NORMAL, l==null?"":l.getName(), launcher, RetentionStrategy.NOOP);
            hudson.addNode(slave);
            return slave;
        }
    }

    /**
     * Returns the last item in the list.
     */
    protected <T> T last(List<T> items) {
        return items.get(items.size()-1);
    }

    /**
     * Pauses the execution until ENTER is hit in the console.
     * <p>
     * This is often very useful so that you can interact with Hudson
     * from an browser, while developing a test case.
     */
    protected void pause() throws IOException {
        new BufferedReader(new InputStreamReader(System.in)).readLine();
    }<commit_msg>applied a patch from <NAME>. See http://www.nabble.com/-PATCH--environment-expansion-in-job-configuration-td21872605.html<commit_after>/*
 * The MIT License
 * 
 * Copyright (c) 2004-2009, Sun Microsystems, Inc., <NAME>, <NAME>
 * 
 * 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 org.jvnet.hudson.test;

import com.gargoylesoftware.htmlunit.AjaxController;
import com.gargoylesoftware.htmlunit.BrowserVersion;
import com.gargoylesoftware.htmlunit.FailingHttpStatusCodeException;
import com.gargoylesoftware.htmlunit.Page;
import com.gargoylesoftware.htmlunit.WebRequestSettings;
import com.gargoylesoftware.htmlunit.html.HtmlButton;
import com.gargoylesoftware.htmlunit.html.HtmlForm;
import com.gargoylesoftware.htmlunit.html.HtmlPage;
import com.gargoylesoftware.htmlunit.javascript.JavaScriptEngine;
import com.gargoylesoftware.htmlunit.javascript.host.Stylesheet;
import com.gargoylesoftware.htmlunit.javascript.host.XMLHttpRequest;
import hudson.CloseProofOutputStream;
import hudson.FilePath;
import hudson.Functions;
import hudson.Launcher.LocalLauncher;
import hudson.WebAppMain;
import hudson.matrix.MatrixProject;
import hudson.maven.MavenModuleSet;
import hudson.maven.MavenReporters;
import hudson.model.Descriptor;
import hudson.model.FreeStyleProject;
import hudson.model.Hudson;
import hudson.model.Item;
import hudson.model.JDK;
import hudson.model.Label;
import hudson.model.Node.Mode;
import hudson.model.Result;
import hudson.model.Run;
import hudson.model.Saveable;
import hudson.model.TaskListener;
import hudson.model.UpdateCenter;
import hudson.slaves.CommandLauncher;
import hudson.slaves.DumbSlave;
import hudson.slaves.RetentionStrategy;
import hudson.tasks.BuildStep;
import hudson.tasks.Mailer;
import hudson.tasks.Maven;
import hudson.tasks.Maven.MavenInstallation;
import hudson.util.ProcessTreeKiller;
import hudson.util.StreamTaskListener;
import junit.framework.TestCase;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.FileUtils;
import org.jvnet.hudson.test.HudsonHomeLoader.CopyExisting;
import org.jvnet.hudson.test.recipes.Recipe;
import org.jvnet.hudson.test.recipes.Recipe.Runner;
import org.jvnet.hudson.test.rhino.JavaScriptDebugger;
import org.mortbay.jetty.Server;
import org.mortbay.jetty.bio.SocketConnector;
import org.mortbay.jetty.security.HashUserRealm;
import org.mortbay.jetty.security.UserRealm;
import org.mortbay.jetty.webapp.Configuration;
import org.mortbay.jetty.webapp.WebAppContext;
import org.mortbay.jetty.webapp.WebXmlConfiguration;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.ContextFactory;
import org.w3c.css.sac.CSSException;
import org.w3c.css.sac.CSSParseException;
import org.w3c.css.sac.ErrorHandler;
import org.xml.sax.SAXException;
import org.kohsuke.stapler.MetaClass;
import org.kohsuke.stapler.Dispatcher;
import org.kohsuke.stapler.MetaClassLoader;

import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.lang.annotation.Annotation;
import java.lang.ref.WeakReference;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.jar.Manifest;
import java.util.logging.Filter;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;

/**
 * Base class for all Hudson test cases.
 *
 * @see <a href="http://hudson.gotdns.com/wiki/display/HUDSON/Unit+Test">Wiki article about unit testing in Hudson</a>
 * @author <NAME>
 */
public abstract class HudsonTestCase extends TestCase {
    public Hudson hudson;

    protected final TestEnvironment env = new TestEnvironment();
    protected HudsonHomeLoader homeLoader = HudsonHomeLoader.NEW;
    /**
     * TCP/IP port that the server is listening on.
     */
    protected int localPort;
    protected Server server;

    /**
     * Where in the {@link Server} is Hudson deploed?
     */
    protected String contextPath = "/";

    /**
     * {@link Runnable}s to be invoked at {@link #tearDown()}.
     */
    protected List<LenientRunnable> tearDowns = new ArrayList<LenientRunnable>();

    protected List<Runner> recipes = new ArrayList<Runner>();

    /**
     * Remember {@link WebClient}s that are created, to release them properly.
     */
    private List<WeakReference<WebClient>> clients = new ArrayList<WeakReference<WebClient>>();

    /**
     * JavaScript "debugger" that provides you information about the JavaScript call stack
     * and the current values of the local variables in those stack frame.
     *
     * <p>
     * Unlike Java debugger, which you as a human interfaces directly and interactively,
     * this JavaScript debugger is to be interfaced by your program (or through the
     * expression evaluation capability of your Java debugger.) 
     */
    protected JavaScriptDebugger jsDebugger = new JavaScriptDebugger();

    protected HudsonTestCase(String name) {
        super(name);
    }

    protected HudsonTestCase() {
    }

    protected void setUp() throws Exception {
        env.pin();
        recipe();
        hudson = newHudson();
        hudson.setNoUsageStatistics(true); // collecting usage stats from tests are pointless.
        hudson.servletContext.setAttribute("app",hudson);
        hudson.servletContext.setAttribute("version","?");
        WebAppMain.installExpressionFactory(new ServletContextEvent(hudson.servletContext));

        // set a default JDK to be the one that the harness is using.
        hudson.getJDKs().add(new JDK("default",System.getProperty("java.home")));

        // cause all the descriptors to reload.
        // ideally we'd like to reset them to properly emulate the behavior, but that's not possible.
        Mailer.DESCRIPTOR.setHudsonUrl(null);
        for( Descriptor d : Descriptor.ALL )
            d.load();
    }

    protected void tearDown() throws Exception {
        // cancel pending asynchronous operations, although this doesn't really seem to be working
        for (WeakReference<WebClient> client : clients) {
            WebClient c = client.get();
            if(c==null) continue;
            // unload the page to cancel asynchronous operations 
            c.getPage("about:blank");
        }
        clients.clear();

        server.stop();
        for (LenientRunnable r : tearDowns)
            r.run();

        // TODO: avoid relying on singletons and switch to some DI container.
        // In the mean time, discard descriptors created during this exercise.
        // without this, plugins loaded in the tests will be left and interferes with the later tests.
        cleanUpDescriptors(Descriptor.ALL);
        cleanUpDescriptors(BuildStep.PUBLISHERS);
        cleanUpDescriptors(MavenReporters.LIST);

        hudson.cleanUp();
        env.dispose();
    }

    private void cleanUpDescriptors(Iterable<? extends Descriptor> cont) {
        ClassLoader base = getClass().getClassLoader();
        for (Iterator<? extends Descriptor> itr = cont.iterator(); itr.hasNext();) {
            Descriptor d =  itr.next();
            ClassLoader cl = d.getClass().getClassLoader();
            if(cl==base)    continue;

            while(cl!=null) {
                cl = cl.getParent();
                if(cl==base) {
                    itr.remove();
                    break;
                }
            }
        }
    }

    protected void runTest() throws Throwable {
        System.out.println("=== Starting "+getName());
        new JavaScriptEngine(null);   // ensure that ContextFactory is initialized
        Context cx= ContextFactory.getGlobal().enterContext();
        try {
            cx.setOptimizationLevel(-1);
            cx.setDebugger(jsDebugger,null);

            super.runTest();
        } finally {
            Context.exit();
        }
    }

    /**
     * Creates a new instance of {@link Hudson}. If the derived class wants to create it in a different way,
     * you can override it.
     */
    protected Hudson newHudson() throws Exception {
        File home = homeLoader.allocate();
        for (Runner r : recipes)
            r.decorateHome(this,home);
        return new Hudson(home, createWebServer());
    }

    /**
     * Prepares a webapp hosting environment to get {@link ServletContext} implementation
     * that we need for testing.
     */
    protected ServletContext createWebServer() throws Exception {
        server = new Server();

        WebAppContext context = new WebAppContext(WarExploder.getExplodedDir().getPath(), contextPath);
        context.setClassLoader(getClass().getClassLoader());
        context.setConfigurations(new Configuration[]{new WebXmlConfiguration(),new NoListenerConfiguration()});
        server.setHandler(context);

        SocketConnector connector = new SocketConnector();
        server.addConnector(connector);
        server.addUserRealm(configureUserRealm());
        server.start();

        localPort = connector.getLocalPort();

        return context.getServletContext();
    }

    /**
     * Configures a security realm for a test.
     */
    protected UserRealm configureUserRealm() {
        HashUserRealm realm = new HashUserRealm();
        realm.setName("default");   // this is the magic realm name to make it effective on everywhere
        realm.put("alice","alice");
        realm.put("bob","bob");
        realm.put("charlie","charlie");

        realm.addUserToRole("alice","female");
        realm.addUserToRole("bob","male");
        realm.addUserToRole("charlie","male");

        return realm;
    }

    /**
     * Locates Maven2 and configure that as the only Maven in the system.
     */
    protected void configureDefaultMaven() throws Exception {
        // first if we are running inside Maven, pick that Maven.
        String home = System.getProperty("maven.home");
        if(home!=null) {
            Maven.DESCRIPTOR.setInstallations(new MavenInstallation("default",home));
            return;
        }

        // otherwise extract the copy we have.
        // this happens when a test is invoked from an IDE, for example.
        LOGGER.warning("Extracting a copy of Maven bundled in the test harness. " +
                "To avoid a performance hit, set the system property 'maven.home' to point to a Maven2 installation.");
        FilePath mvn = hudson.getRootPath().createTempFile("maven", "zip");
        OutputStream os = mvn.write();
        try {
            IOUtils.copy(HudsonTestCase.class.getClassLoader().getResourceAsStream("maven-2.0.7-bin.zip"), os);
        } finally {
            os.close();
        }
        File mvnHome = createTmpDir();
        mvn.unzip(new FilePath(mvnHome));

        Maven.DESCRIPTOR.setInstallations(new MavenInstallation("default",
                new File(mvnHome,"maven-2.0.7").getAbsolutePath()));
    }

//
// Convenience methods
//

    protected FreeStyleProject createFreeStyleProject() throws IOException {
        return createFreeStyleProject(createUniqueProjectName());
    }

    protected FreeStyleProject createFreeStyleProject(String name) throws IOException {
        return (FreeStyleProject)hudson.createProject(FreeStyleProject.DESCRIPTOR,name);
    }

    protected MatrixProject createMatrixProject() throws IOException {
        return createMatrixProject(createUniqueProjectName());
    }

    protected MatrixProject createMatrixProject(String name) throws IOException {
        return (MatrixProject)hudson.createProject(MatrixProject.DESCRIPTOR,name);
    }

    /**
     * Creates a empty Maven project with an unique name.
     *
     * @see #configureDefaultMaven()
     */
    protected MavenModuleSet createMavenProject() throws IOException {
        return createMavenProject(createUniqueProjectName());
    }

    /**
     * Creates a empty Maven project with the given name.
     *
     * @see #configureDefaultMaven()
     */
    protected MavenModuleSet createMavenProject(String name) throws IOException {
        return (MavenModuleSet)hudson.createProject(MavenModuleSet.DESCRIPTOR,name);
    }

    private String createUniqueProjectName() {
        return "test"+hudson.getItems().size();
    }

    /**
     * Creates {@link LocalLauncher}. Useful for launching processes.
     */
    protected LocalLauncher createLocalLauncher() {
        return new LocalLauncher(new StreamTaskListener(System.out));
    }

    /**
     * Allocates a new temporary directory for the duration of this test.
     */
    public File createTmpDir() throws IOException {
        return env.temporaryDirectoryAllocator.allocate();
    }

    public DumbSlave createSlave() throws Exception {
        return createSlave(null);
    }

    /**
     * Creates and launches a new slave on the local host.
     */
    public DumbSlave createSlave(Label l) throws Exception {
        CommandLauncher launcher = new CommandLauncher(
                System.getProperty("java.home") + "/bin/java -jar " + new File(hudson.getJnlpJars("slave.jar").getURL().getPath()).getPath());

        // this synchronization block is so that we don't end up adding the same slave name more than once.
        synchronized (hudson) {
            DumbSlave slave = new DumbSlave("slave" + hudson.getNodes().size(), "dummy",
                    createTmpDir().getPath(), "1", Mode.NORMAL, l==null?"":l.getName(), launcher, RetentionStrategy.NOOP);
            hudson.addNode(slave);
            return slave;
        }
    }

    /**
     * Returns the last item in the list.
     */
    protected <T> T last(List<T> items) {
        return items.get(items.size()-1);
    }

    /**
     * Pauses the execution until ENTER is hit in the console.
     * <p>
     * This is often very useful so that you can interact with Hudson
     * from an browser, while developing a test case.
     */
    protected void pause() throws IOException {
        new BufferedReader(new InputStreamReader(System.in)).readLine();
    }
{'fraction_non_alphanumeric': 0.09858366271409749, 'fraction_numerical': 0.0017127799736495388, 'mean_word_length': 3.172759758108851, 'pattern_counts': {'":': 2, '<': 44, '<?xml version=': 0, '>': 44, 'https://': 0, 'lorem ipsum': 0, 'www.': 1, 'xml': 2}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4625561', 'n_tokens_mistral': 8638, 'n_tokens_neox': 8240, 'n_words': 2315}