提交 92f48ca2 编写于 作者: J Jesse Glick

Use released version of test harness.

上级 c87cd281
......@@ -50,8 +50,6 @@ THE SOFTWARE.
<modules>
<module>core</module>
<module>war</module>
<module>test-harness</module>
<module>test-harness-tools</module>
<module>test</module>
<module>cli</module>
<module>plugins</module>
......
<?xml version="1.0" encoding="UTF-8"?>
<!--
The MIT License
Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi
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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.jenkins-ci.main</groupId>
<artifactId>pom</artifactId>
<version>1.645-SNAPSHOT</version>
</parent>
<artifactId>jenkins-test-harness-tools</artifactId>
<name>Test harness tools</name>
<description>Tool installations that may be used by functional tests.</description>
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>jenkins-test-harness</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>ant</artifactId>
<version>1.2</version>
</dependency>
</dependencies>
</project>
/*
* The MIT License
*
* Copyright 2016 CloudBees, Inc.
*
* 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 hudson.FilePath;
import hudson.Functions;
import hudson.Launcher;
import hudson.tasks.Ant;
import hudson.tasks.Maven;
import hudson.util.StreamTaskListener;
import hudson.util.jna.GNUCLibrary;
import java.io.File;
import java.util.logging.Level;
import java.util.logging.Logger;
import jenkins.model.Jenkins;
import org.junit.rules.TemporaryFolder;
/**
* Utility to install standard tools in the Jenkins under test.
*/
public class ToolInstallations {
private static final Logger LOGGER = Logger.getLogger(ToolInstallations.class.getName());
/**
* Returns the older default Maven, while still allowing specification of
* other bundled Mavens.
*/
public static Maven.MavenInstallation configureDefaultMaven() throws Exception {
return configureDefaultMaven("apache-maven-2.2.1", Maven.MavenInstallation.MAVEN_20);
}
public static Maven.MavenInstallation configureMaven3() throws Exception {
Maven.MavenInstallation mvn = configureDefaultMaven("apache-maven-3.0.1", Maven.MavenInstallation.MAVEN_30);
Maven.MavenInstallation m3 = new Maven.MavenInstallation("apache-maven-3.0.1", mvn.getHome(), JenkinsRule.NO_PROPERTIES);
Jenkins.getInstance().getDescriptorByType(Maven.DescriptorImpl.class).setInstallations(m3);
return m3;
}
/**
* Locates Maven2 and configure that as the only Maven in the system.
*/
public static Maven.MavenInstallation configureDefaultMaven(String mavenVersion, int mavenReqVersion) throws Exception {
// first if we are running inside Maven, pick that Maven, if it meets the criteria we require..
File buildDirectory = new File(System.getProperty("buildDirectory", "target")); // TODO relative path
File mvnHome = new File(buildDirectory, mavenVersion);
if (mvnHome.exists()) {
Maven.MavenInstallation mavenInstallation = new Maven.MavenInstallation("default", mvnHome.getAbsolutePath(), JenkinsRule.NO_PROPERTIES);
Jenkins.getInstance().getDescriptorByType(Maven.DescriptorImpl.class).setInstallations(mavenInstallation);
return mavenInstallation;
}
// Does maven.home point to a Maven installation which satisfies mavenReqVersion?
String home = System.getProperty("maven.home");
if (home != null) {
Maven.MavenInstallation mavenInstallation = new Maven.MavenInstallation("default", home, JenkinsRule.NO_PROPERTIES);
if (mavenInstallation.meetsMavenReqVersion(new Launcher.LocalLauncher(StreamTaskListener.fromStdout()), mavenReqVersion)) {
Jenkins.getInstance().getDescriptorByType(Maven.DescriptorImpl.class).setInstallations(mavenInstallation);
return mavenInstallation;
}
}
// otherwise extract the copy we have.
// this happens when a test is invoked from an IDE, for example.
LOGGER.log(Level.WARNING,"Extracting a copy of Maven bundled in the test harness into {0}. "
+ "To avoid a performance hit, set the system property ''maven.home'' to point to a Maven2 installation.", mvnHome);
FilePath mvn = Jenkins.getInstance().getRootPath().createTempFile("maven", "zip");
mvn.copyFrom(JenkinsRule.class.getClassLoader().getResource(mavenVersion + "-bin.zip"));
mvn.unzip(new FilePath(buildDirectory));
// TODO: switch to tar that preserves file permissions more easily
try {
GNUCLibrary.LIBC.chmod(new File(mvnHome, "bin/mvn").getPath(), 0755);
} catch (LinkageError x) {
// skip; TODO 1.630+ can use Functions.isGlibcSupported
}
Maven.MavenInstallation mavenInstallation = new Maven.MavenInstallation("default",
mvnHome.getAbsolutePath(), JenkinsRule.NO_PROPERTIES);
Jenkins.getInstance().getDescriptorByType(Maven.DescriptorImpl.class).setInstallations(mavenInstallation);
return mavenInstallation;
}
/**
* Extracts Ant and configures it.
*/
public static Ant.AntInstallation configureDefaultAnt(TemporaryFolder tmp) throws Exception {
Ant.AntInstallation antInstallation;
if (System.getenv("ANT_HOME") != null) {
antInstallation = new Ant.AntInstallation("default", System.getenv("ANT_HOME"), JenkinsRule.NO_PROPERTIES);
} else {
LOGGER.warning("Extracting a copy of Ant bundled in the test harness. "
+ "To avoid a performance hit, set the environment variable ANT_HOME to point to an Ant installation.");
FilePath ant = Jenkins.getInstance().getRootPath().createTempFile("ant", "zip");
ant.copyFrom(JenkinsRule.class.getClassLoader().getResource("apache-ant-1.8.1-bin.zip"));
File antHome = tmp.newFolder("antHome");
ant.unzip(new FilePath(antHome));
// TODO: switch to tar that preserves file permissions more easily
try {
GNUCLibrary.LIBC.chmod(new File(antHome, "apache-ant-1.8.1/bin/ant").getPath(), 0755);
} catch (LinkageError x) {
// skip; TODO 1.630+ can use Functions.isGlibcSupported
}
antInstallation = new Ant.AntInstallation("default", new File(antHome, "apache-ant-1.8.1").getAbsolutePath(), JenkinsRule.NO_PROPERTIES);
}
Jenkins.getInstance().getDescriptorByType(Ant.DescriptorImpl.class).setInstallations(antInstallation);
return antInstallation;
}
private ToolInstallations() {
}
}
<?xml version="1.0" encoding="UTF-8"?>
<!--
The MIT License
Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi
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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.jenkins-ci.main</groupId>
<artifactId>pom</artifactId>
<version>1.645-SNAPSHOT</version>
</parent>
<artifactId>jenkins-test-harness</artifactId>
<name>Test harness for Jenkins and plugins</name>
<description>Harness used to run functional tests of Jenkins core and plugins.</description>
<dependencies>
<dependency>
<!--
put hudson.war in the classpath. we can't pull in the war artifact directly
because Maven excludes all wars from classpath automatically. so we need a jar artifact.
-->
<groupId>${project.groupId}</groupId>
<artifactId>jenkins-war</artifactId>
<version>1.580.1</version>
<classifier>war-for-test</classifier>
<exclusions>
<exclusion>
<groupId>org.jenkins-ci.modules</groupId>
<artifactId>sshd</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.mortbay.jetty</groupId>
<artifactId>jetty</artifactId>
<version>6.1.26</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-library</artifactId>
<version>1.3</version>
</dependency>
<dependency>
<groupId>net.sourceforge.htmlunit</groupId>
<artifactId>htmlunit</artifactId>
<version>2.18</version>
<exclusions>
<exclusion>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.jvnet.hudson</groupId>
<artifactId>embedded-rhino-debugger</artifactId>
<version>1.2</version>
<exclusions>
<exclusion>
<groupId>org.jvnet.hudson</groupId>
<artifactId>htmlunit-core-js</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.netbeans.modules</groupId>
<artifactId>org-netbeans-insane</artifactId>
<version>RELEASE72</version>
</dependency>
<dependency>
<groupId>com.github.stephenc.findbugs</groupId>
<artifactId>findbugs-annotations</artifactId>
<version>1.3.9-1</version>
</dependency>
<dependency> <!-- TODO can we switch this to use Aether directly? -->
<groupId>org.jenkins-ci.lib</groupId>
<artifactId>lib-jenkins-maven-embedder</artifactId>
<version>3.11</version>
<exclusions>
<exclusion>
<groupId>org.sonatype.sisu</groupId>
<artifactId>sisu-guice</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>matrix-auth</artifactId>
<version>1.0.2</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.kohsuke.stapler</groupId>
<artifactId>maven-stapler-plugin</artifactId>
<!-- version specified in grandparent pom -->
<extensions>true</extensions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<!-- version specified in grandparent pom -->
<configuration>
<argLine>-Dfile.encoding=UTF-8 -Xmx256m -XX:MaxPermSize=128m</argLine>
<systemPropertyVariables>
<!-- use AntClassLoader that supports predictable file handle release -->
<hudson.ClassicPluginStrategy.useAntClassLoader>true</hudson.ClassicPluginStrategy.useAntClassLoader>
<hudson.maven.debug>${mavenDebug}</hudson.maven.debug>
<buildDirectory>${project.build.directory}</buildDirectory>
<ignore.random.failures>${ignore.random.failures}</ignore.random.failures>
</systemPropertyVariables>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.gmaven</groupId>
<artifactId>gmaven-plugin</artifactId>
<!-- version specified in grandparent pom -->
<executions>
<execution>
<id>preset-packager</id>
<phase>process-resources</phase>
<goals>
<goal>execute</goal>
</goals>
<configuration>
<source>${pom.basedir}/src/main/preset-data/package.groovy</source>
</configuration>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.apache.ant</groupId>
<artifactId>ant</artifactId>
<version>1.8.0</version>
</dependency>
<!-- Usually a dependency of ant, but some people seem to have an incomplete ant POM. See JENKINS-11416 -->
<dependency>
<groupId>org.apache.ant</groupId>
<artifactId>ant-launcher</artifactId>
<version>1.8.0</version>
</dependency>
<dependency>
<groupId>org.codehaus.gmaven.runtime</groupId>
<artifactId>gmaven-runtime-2.0</artifactId>
<version>1.5-jenkins-1</version>
</dependency>
</dependencies>
<configuration>
<providerSelection>2.0</providerSelection>
</configuration>
</plugin>
</plugins>
</build>
</project>
# Overview
Jenkins used to maintain a forked version of HtmlUnit and use it via `JenkinsRule` (and `HudsonTestCase`). Moving
away from the forked version has allowed Jenkins GUI to start using more modern JavaScript libraries than were
possible through the forked version.
Moving away from the forked version also means that some tests in plugins may no longer compile due to the
fact that they were using some methods that were added to the fork, but not available in the mainstream HtmlUnit.
To help with that, the test harness now provides static utilities for all of this functionality.
# WebClientUtil
The `WebClientUtil` class provides static utilities for interacting asynchronously with HtmlUnit's `WebClient`,
supporting `waitForJSExec` calls to allow your test code wait for background (asynchronous) JavaScript code to complete
before the test continues. Calling these methods are not required in many cases as they are called for you from
other static utilities (e.g. `HtmlFormUtil.submit`), but sometimes it is required to call them directly.
Because HtmlUnit executes JavaScript asynchronously, it's usually not possible to block and catch exceptions. For
that reason, `WebClientUtil` provides the `addExceptionListener` utility as a way of registering an exception listener.
This typically needs to be used in conjunction with the `waitForJSExec` method e.g.
```java
WebClient webClient = jenkinsRule.createWebClient();
WebClientUtil.ExceptionListener exceptionListener = WebClientUtil.addExceptionListener(webClient);
// Interact with Jenkins UI as normal in a test...
// Make sure all background JavaScript has completed so as expected exceptions have been thrown.
WebClientUtil.waitForJSExec(webClient);
// Now we can check for exceptions etc...
exceptionListener.assertHasException();
ScriptException e = exceptionListener.getScriptException();
Assert.assertTrue(e.getMessage().contains("simulated error"));
```
# HtmlElementUtil, HtmlFormUtil and DomNodeUtil
These classes provide static utility methods to replace functionality that was added to the forked HtmlUnit e.g.
for triggering `<form>` submit, triggering a click event on an element etc.
Use your IDE to access these utility classes and the static methods available.
\ No newline at end of file
/*
* The MIT License
*
* Copyright (c) 2015, CloudBees, Inc.
*
* 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 com.gargoylesoftware.htmlunit;
import com.gargoylesoftware.htmlunit.javascript.JavaScriptErrorListener;
import org.junit.Assert;
import java.net.MalformedURLException;
import java.net.URL;
/**
* {@link WebClient} helper methods.
* @author <a href="mailto:tom.fennelly@gmail.com">tom.fennelly@gmail.com</a>
*/
public class WebClientUtil {
/**
* Wait for all async JavaScript tasks associated with the supplied {@link WebClient} instance
* to complete.
* <p>
* Waits for 10 seconds before timing out.
* @param webClient The {@link WebClient} instance.
*/
public static void waitForJSExec(WebClient webClient) {
waitForJSExec(webClient, 10000);
}
/**
* Wait for all async JavaScript tasks associated with the supplied {@link WebClient} instance
* to complete.
* @param webClient The {@link WebClient} instance.
* @param timeout The timeout in milliseconds.
*/
public static void waitForJSExec(WebClient webClient, long timeout) {
webClient.getJavaScriptEngine().processPostponedActions();
webClient.waitForBackgroundJavaScript(timeout);
}
/**
* Create and add an {@link ExceptionListener} to the {@link WebClient} instance.
* @param webClient The {@link WebClient} instance.
* @return The {@link ExceptionListener}.
*/
public static ExceptionListener addExceptionListener(WebClient webClient) {
ExceptionListener exceptionListener = new ExceptionListener(webClient);
webClient.setJavaScriptErrorListener(exceptionListener);
return exceptionListener;
}
/**
* JavaScript Exception listener.
* @see #addExceptionListener(WebClient)
*/
public static class ExceptionListener implements JavaScriptErrorListener {
private final WebClient webClient;
private ScriptException scriptException;
private ExceptionListener(WebClient webClient) {
this.webClient = webClient;
}
/**
* Get the last {@link ScriptException}.
* @return The last {@link ScriptException}, or {@code null} if none happened.
*/
public ScriptException getScriptException() {
return scriptException;
}
/**
* Get the last {@link ScriptException}.
* <p>
* Performs a call to {@link #assertHasException()}.
* @return The last {@link ScriptException}.
*/
public ScriptException getExpectedScriptException() {
assertHasException();
return scriptException;
}
/**
* {@inheritDoc}
*/
@Override
public void scriptException(InteractivePage htmlPage, ScriptException scriptException) {
this.scriptException = scriptException;
}
/**
* Assert that a {@link ScriptException} occurred within the JavaScript executing
* on the associated {@link WebClient}.
*/
public void assertHasException() {
WebClientUtil.waitForJSExec(webClient);
Assert.assertNotNull("A JavaScript Exception was expected.", scriptException);
}
/**
* {@inheritDoc}
*/
@Override
public void timeoutError(InteractivePage htmlPage, long allowedTime, long executionTime) {
}
/**
* {@inheritDoc}
*/
@Override
public void malformedScriptURL(InteractivePage htmlPage, String url, MalformedURLException malformedURLException) {
}
/**
* {@inheritDoc}
*/
@Override
public void loadScriptError(InteractivePage htmlPage, URL scriptUrl, Exception exception) {
}
}
}
package com.gargoylesoftware.htmlunit;
import org.junit.Assert;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* @author <a href="mailto:tom.fennelly@gmail.com">tom.fennelly@gmail.com</a>
*/
public interface WebResponseListener {
void onLoadWebResponse(WebRequest webRequest, WebResponse webResponse) throws IOException;
public final class StatusListener implements WebResponseListener {
private final int statusCode;
private final List<WebResponse> responses = new CopyOnWriteArrayList<>();
public StatusListener(final int statusCode) {
this.statusCode = statusCode;
}
@Override
public void onLoadWebResponse(WebRequest webRequest, WebResponse webResponse) throws IOException {
if (webResponse.getStatusCode() == statusCode) {
responses.add(webResponse);
}
}
public void assertHasResponses() {
Assert.assertTrue(!responses.isEmpty());
}
public List<WebResponse> getResponses() {
return responses;
}
}
}
/*
* The MIT License
*
* Copyright (c) 2015, CloudBees, Inc.
*
* 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 com.gargoylesoftware.htmlunit.html;
import com.gargoylesoftware.htmlunit.WebClientUtil;
import com.gargoylesoftware.htmlunit.html.xpath.XPathUtils;
import java.util.List;
/**
* {@link DomNode} helper methods.
*
* @author <a href="mailto:tom.fennelly@gmail.com">tom.fennelly@gmail.com</a>
*/
public class DomNodeUtil {
/**
* Evaluates an XPath expression from the specified node, returning the resultant nodes.
* <p>
* Calls {@link WebClientUtil#waitForJSExec(com.gargoylesoftware.htmlunit.WebClient)} before
* executing the query.
*
* @param domNode the node to start searching from
* @param xpathExpr the XPath expression
* @return the list of objects found.
*/
public static <E> List<E> selectNodes(final DomNode domNode, final String xpathExpr) {
WebClientUtil.waitForJSExec(domNode.getPage().getWebClient());
return (List) XPathUtils.getByXPath(domNode, xpathExpr, null);
}
/**
* Evaluates the specified XPath expression from this node, returning the first matching element,
* or <tt>null</tt> if no node matches the specified XPath expression.
* <p>
* Calls {@link WebClientUtil#waitForJSExec(com.gargoylesoftware.htmlunit.WebClient)} before
* executing the query.
*
* @param domNode the node to start searching from
* @param xpathExpr the XPath expression
* @return the first element matching the specified XPath expression
*/
public static <X> X selectSingleNode(final DomNode domNode, final String xpathExpr) {
WebClientUtil.waitForJSExec(domNode.getPage().getWebClient());
return domNode.<X>getFirstByXPath(xpathExpr);
}
}
/*
* The MIT License
*
* Copyright (c) 2015, CloudBees, Inc.
*
* 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 com.gargoylesoftware.htmlunit.html;
import com.gargoylesoftware.htmlunit.Page;
import com.gargoylesoftware.htmlunit.WebClient;
import com.gargoylesoftware.htmlunit.WebClientUtil;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
/**
* {@link HtmlElement} helper methods.
* @author <a href="mailto:tom.fennelly@gmail.com">tom.fennelly@gmail.com</a>
*/
public class HtmlElementUtil {
/**
* Click on the supplied element.
* <p>
* Waits for all executing JavaScript tasks to complete before returning.
*
* @param element The element to click.
* @return The page resulting from the click
* @throws IOException if an IO error occurs
*/
public static Page click(HtmlElement element) throws IOException {
if (element == null) {
return null;
}
try {
return element.click();
} finally {
// The JS script execution tasks are added to a queue and executed
// async. Wait for all to finish executing.
WebClient webClient = element.getPage().getWebClient();
WebClientUtil.waitForJSExec(webClient);
}
}
/**
* Does the supplied element define the specified HTML "class" name.
* @param element The element to check.
* @param className The HTML "class" name to check for.
* @return {@code true} if the element defines the specified class, otherwise {@code false}.
*/
public static boolean hasClassName(HtmlElement element, String className) {
String classAttribute = element.getAttribute("class");
Set<String> classes = new HashSet<>(Arrays.asList(classAttribute.split(" ")));
return classes.contains(className);
}
}
/*
* The MIT License
*
* Copyright (c) 2015, CloudBees, Inc.
*
* 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 com.gargoylesoftware.htmlunit.html;
import com.gargoylesoftware.htmlunit.ElementNotFoundException;
import com.gargoylesoftware.htmlunit.Page;
import com.gargoylesoftware.htmlunit.WebClient;
import com.gargoylesoftware.htmlunit.WebClientUtil;
import java.io.IOException;
import java.util.List;
/**
* {@link HtmlForm} helper functions.
* <p>
* In many cases, the methods defined here replace methods of the same name that were
* added to the {@link HtmlForm} class on the old forked version of HtmlUnit.
*
* @author <a href="mailto:tom.fennelly@gmail.com">tom.fennelly@gmail.com</a>
*/
public class HtmlFormUtil {
/**
* Submit the supplied {@link HtmlForm}.
* <p>
* Locates the submit element/button on the form.
* @param htmlForm The {@link HtmlForm}.
* @return The submit result page.
* @throws IOException Error performing submit.
*/
public static Page submit(final HtmlForm htmlForm) throws IOException {
HtmlElement submitElement = getSubmitButton(htmlForm);
return submit(htmlForm, submitElement);
}
/**
* Submit the supplied {@link HtmlForm} via the supplied submit element.
* @param htmlForm The {@link HtmlForm}.
* @param submitElement The element through which the submit should be performed.
* @return The submit result page.
* @throws IOException Error performing submit.
*/
public static Page submit(HtmlForm htmlForm, HtmlElement submitElement) throws IOException {
final HtmlPage htmlPage = (HtmlPage) htmlForm.getPage();
final WebClient webClient = htmlPage.getWebClient();
try {
if (submitElement != null && !(submitElement instanceof SubmittableElement)) {
// Just click and return
return submitElement.click();
}
try {
htmlForm.submit((SubmittableElement) submitElement);
} finally {
// The HtmlForm submit doesn't really do anything. It just adds a "LoadJob"
// to an internal queue. What we are doing here is manually forcing the load of
// the response for that submit LoadJob and then getting the enclosing page
// from the current window on the WebClient, allowing us to return the correct
// HtmlPage object to the test.
webClient.loadDownloadedResponses();
Page resultPage = webClient.getCurrentWindow().getEnclosedPage();
if (resultPage == htmlPage) {
return HtmlElementUtil.click(submitElement);
} else {
return resultPage;
}
}
} finally {
// Make sure all background JS has executed.
WebClientUtil.waitForJSExec(webClient);
}
}
/**
* Returns all the &lt;input type="submit"> elements in this form.
*/
public static List<HtmlElement> getSubmitButtons(final HtmlForm htmlForm) throws ElementNotFoundException {
final List<HtmlElement> list = htmlForm.getElementsByAttribute("input", "type", "submit");
// collect inputs from lost children
for (final HtmlElement elt : htmlForm.getLostChildren()) {
list.add(elt);
}
return list;
}
/**
* Gets the first &lt;input type="submit"> element in this form.
*/
public static HtmlElement getSubmitButton(final HtmlForm htmlForm) throws ElementNotFoundException {
List<HtmlElement> submitButtons = getSubmitButtons(htmlForm);
if (!submitButtons.isEmpty()) {
return submitButtons.get(0);
}
for (HtmlElement element : htmlForm.getHtmlElementsByTagName("button")) {
if(element instanceof HtmlButton) {
return element;
}
}
return null;
}
/**
* Get the form button having the specified text/caption.
* @param htmlForm The form containing the button.
* @param caption The button text/caption being searched for.
* @return The button if found.
* @throws ElementNotFoundException Failed to find the button on the form.
*/
public static HtmlButton getButtonByCaption(final HtmlForm htmlForm, final String caption) throws ElementNotFoundException {
for (HtmlElement b : htmlForm.getHtmlElementsByTagName("button")) {
if(b instanceof HtmlButton && b.getTextContent().trim().equals(caption)) {
return (HtmlButton) b;
}
}
throw new ElementNotFoundException("button", "caption", caption);
}
}
/*
* The MIT License
*
* Copyright 2013 Red Hat, Inc.
*
* 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 hudson.cli;
import hudson.Extension;
import hudson.model.User;
import hudson.security.ACL;
import hudson.security.AuthorizationStrategy;
import hudson.security.Permission;
import hudson.security.SidACL;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.PrintStream;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import jenkins.model.Jenkins;
import org.acegisecurity.acls.sid.PrincipalSid;
import org.acegisecurity.acls.sid.Sid;
import org.hamcrest.Description;
import org.hamcrest.TypeSafeMatcher;
import org.jvnet.hudson.test.JenkinsRule;
/**
* Helper class to invoke {@link CLICommand} and check the response.
*
* @author ogondza
*/
public class CLICommandInvoker {
private static final String username = "user";
private final JenkinsRule rule;
private final CLICommand command;
private InputStream stdin;
private List<String> args = Collections.emptyList();
private List<Permission> permissions = Collections.emptyList();
private Locale locale = Locale.ENGLISH;
public CLICommandInvoker(final JenkinsRule rule, final CLICommand command) {
if (command.getClass().getAnnotation(Extension.class) == null) {
throw new AssertionError(String.format(
"Command %s is missing @Extension annotation.",
command.getClass()
));
}
this.rule = rule;
this.command = command;
}
public CLICommandInvoker(final JenkinsRule rule, final String command) {
this.rule = rule;
this.command = CLICommand.clone(command);
if (this.command == null) throw new AssertionError("No such command: " + command);
}
public CLICommandInvoker authorizedTo(final Permission... permissions) {
this.permissions = Arrays.asList(permissions);
return this;
}
public CLICommandInvoker withStdin(final InputStream stdin) {
if (stdin == null) throw new NullPointerException("No stdin provided");
this.stdin = stdin;
return this;
}
public CLICommandInvoker withArgs(final String... args) {
this.args = Arrays.asList(args);
return this;
}
public Result invokeWithArgs(final String... args) {
return withArgs(args).invoke();
}
public Result invoke() {
setAuth();
final ByteArrayOutputStream out = new ByteArrayOutputStream();
final ByteArrayOutputStream err = new ByteArrayOutputStream();
final int returnCode = command.main(
args, locale, stdin, new PrintStream(out), new PrintStream(err)
);
return new Result(returnCode, out, err);
}
private static class GrantPermissions extends AuthorizationStrategy {
final String username;
final List<Permission> permissions;
GrantPermissions(String username, List<Permission> permissions) {
this.username = username;
this.permissions = permissions;
for (Permission p : permissions) {
p.setEnabled(true);
}
}
@Override
public ACL getRootACL() {
return new SidACL() {
@Override
protected Boolean hasPermission(Sid u, Permission permission) {
if (u instanceof PrincipalSid && ((PrincipalSid) u).getPrincipal().equals(username)) {
for (Permission p = permission; p != null; p = p.impliedBy) {
if (permissions.contains(p)) {
return true;
}
}
}
return false;
}
};
}
@Override
public Collection<String> getGroups() {
return Collections.emptySet();
}
}
private void setAuth() {
if (permissions.isEmpty()) return;
JenkinsRule.DummySecurityRealm realm = rule.createDummySecurityRealm();
realm.addGroups(username, "group");
rule.jenkins.setSecurityRealm(realm);
rule.jenkins.setAuthorizationStrategy(new GrantPermissions(username, permissions));
command.setTransportAuth(user().impersonate());
// Otherwise it is SYSTEM, which would be relevant for a command overriding main:
ACL.impersonate(Jenkins.ANONYMOUS);
}
public User user() {
return User.get(username);
}
public static class Result {
private final int result;
private final ByteArrayOutputStream out;
private final ByteArrayOutputStream err;
private Result(
final int result,
final ByteArrayOutputStream out,
final ByteArrayOutputStream err
) {
this.result = result;
this.out = out;
this.err = err;
}
public int returnCode() {
return result;
}
public String stdout() {
return out.toString();
}
public String stderr() {
return err.toString();
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder("CLI command exited with ").append(result);
String stdout = stdout();
if (!"".equals(stdout)) {
builder.append("\nSTDOUT:\n").append(stdout);
}
String stderr = stderr();
if (!"".equals(stderr)) {
builder.append("\nSTDERR:\n").append(stderr);
}
return builder.toString();
}
}
public abstract static class Matcher extends TypeSafeMatcher<Result> {
private final String description;
private Matcher(String description) {
this.description = description;
}
@Override
protected void describeMismatchSafely(Result result, Description description) {
description.appendText(result.toString());
}
public void describeTo(Description description) {
description.appendText(this.description);
}
public static Matcher hasNoStandardOutput() {
return new Matcher("No standard output") {
@Override protected boolean matchesSafely(Result result) {
return "".equals(result.stdout());
}
};
}
public static Matcher hasNoErrorOutput() {
return new Matcher("No error output") {
@Override protected boolean matchesSafely(Result result) {
return "".equals(result.stderr());
}
};
}
public static Matcher succeeded() {
return new Matcher("Exited with 0 return code") {
@Override protected boolean matchesSafely(Result result) {
return result.result == 0;
}
};
}
public static Matcher succeededSilently() {
return new Matcher("Succeeded silently") {
@Override protected boolean matchesSafely(Result result) {
return result.result == 0 && "".equals(result.stderr()) && "".equals(result.stdout());
}
};
}
public static Matcher failedWith(final long expectedCode) {
return new Matcher("Exited with " + expectedCode + " return code") {
@Override protected boolean matchesSafely(Result result) {
return result.result == expectedCode;
}
};
}
}
}
package hudson.util;
/**
* @author Kohsuke Kawaguchi
*/
public class SecretHelper {
public static void set(String s) {
Secret.SECRET = s;
}
}
package jenkins.model;
/**
* Access the package protected quiet period
*/
public class JenkinsAdaptor {
public static void setQuietPeriod(Jenkins jenkins, int quietPeriod) {
jenkins.quietPeriod = quietPeriod;
}
}
/*
* The MIT License
*
* Copyright (c) 2015 Red Hat, Inc.
*
* 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 jenkins.model;
import hudson.Launcher;
import hudson.model.BuildListener;
import hudson.model.AbstractBuild;
import java.io.IOException;
import org.jvnet.hudson.test.TestBuilder;
/**
* Write text into file in workspace.
*
* @author ogondza
*/
public class WorkspaceWriter extends TestBuilder {
private final String path;
private final String content;
public WorkspaceWriter(String path, String content) {
this.path = path;
this.content = content;
}
@Override
public boolean perform(
AbstractBuild<?, ?> build, Launcher launcher, BuildListener listener
) throws InterruptedException, IOException {
build.getWorkspace().child(path).write(content, "UTF-8");
return true;
}
}
/*
* The MIT License
*
* Copyright 2015 Jesse Glick.
*
* 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 hudson.Extension;
import hudson.console.AnnotatedLargeText;
import hudson.console.LineTransformationOutputStream;
import hudson.model.Run;
import hudson.model.TaskListener;
import hudson.model.listeners.RunListener;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.junit.rules.ExternalResource;
/**
* Echoes build output to standard error as it arrives.
* Usage: <pre>{@code @ClassRule public static BuildWatcher buildWatcher = new BuildWatcher();}</pre>
* Should work in combination with {@link JenkinsRule} or {@link RestartableJenkinsRule}.
* @see JenkinsRule#waitForCompletion
* @see JenkinsRule#waitForMessage
* @since 1.607
*/
public final class BuildWatcher extends ExternalResource {
private static boolean active;
private static final Map<File,RunningBuild> builds = new ConcurrentHashMap<File,RunningBuild>();
private Thread thread;
@Override protected void before() throws Throwable {
active = true;
thread = new Thread("watching builds") {
@Override public void run() {
try {
while (active) {
for (RunningBuild build : builds.values()) {
build.copy();
}
Thread.sleep(50);
}
} catch (InterruptedException x) {
// stopped
}
// last chance
for (RunningBuild build : builds.values()) {
build.copy();
}
}
};
thread.setDaemon(true);
thread.start();
}
@Override protected void after() {
active = false;
thread.interrupt();
}
@Extension public static final class Listener extends RunListener<Run<?,?>> {
@Override public void onStarted(Run<?,?> r, TaskListener listener) {
if (!active) {
return;
}
RunningBuild build = new RunningBuild(r);
RunningBuild orig = builds.put(r.getLogFile(), build);
if (orig != null) {
System.err.println(r + " was started twice?!");
}
}
@Override public void onFinalized(Run<?,?> r) {
if (!active) {
return;
}
RunningBuild build = builds.remove(r.getLogFile());
if (build != null) {
build.copy();
} else {
System.err.println(r + " was finalized but not started?!");
}
}
}
private static final class RunningBuild {
private final AnnotatedLargeText<?> log;
private final OutputStream sink;
private long pos;
RunningBuild(Run<?,?> r) {
log = r.getLogText();
sink = new LogLinePrefixOutputFilter(System.err, "[" + r + "] ");
}
synchronized void copy() {
try {
pos = log.writeLogTo(pos, sink);
// Note that !log.isComplete() after the initial call to copy, even if the build is complete, because Run.getLogText never calls markComplete!
// That is why Run.writeWholeLogTo calls getLogText repeatedly.
// Even if it did call markComplete this might not work from RestartableJenkinsRule since you would have a different Run object after the restart.
// Anyway we can just rely on onFinalized to let us know when to stop.
} catch (FileNotFoundException x) {
// build deleted or not started
} catch (Exception x) {
x.printStackTrace();
}
}
}
// Copied from WorkflowRun.
private static final class LogLinePrefixOutputFilter extends LineTransformationOutputStream {
private final PrintStream logger;
private final String prefix;
LogLinePrefixOutputFilter(PrintStream logger, String prefix) {
this.logger = logger;
this.prefix = prefix;
}
@Override protected void eol(byte[] b, int len) throws IOException {
logger.append(prefix);
logger.write(b, 0, len);
}
}
}
/*
* The MIT License
*
* Copyright (c) 2004-2009, Sun Microsystems, Inc.
*
* 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 hudson.Launcher;
import hudson.Extension;
import hudson.EnvVars;
import hudson.model.AbstractBuild;
import hudson.model.BuildListener;
import hudson.model.Descriptor;
import hudson.tasks.Builder;
import net.sf.json.JSONObject;
import org.kohsuke.stapler.StaplerRequest;
import java.io.IOException;
/**
* {@link Builder} that captures the environment variables used during a build.
*
* @author Kohsuke Kawaguchi
*/
public class CaptureEnvironmentBuilder extends Builder {
private EnvVars envVars;
public EnvVars getEnvVars() {
return envVars;
}
public boolean perform(AbstractBuild<?, ?> build, Launcher launcher, BuildListener listener) throws InterruptedException, IOException {
envVars = build.getEnvironment(listener);
return true;
}
@Extension
public static final class DescriptorImpl extends Descriptor<Builder> {
public Builder newInstance(StaplerRequest req, JSONObject data) {
throw new UnsupportedOperationException();
}
public String getDisplayName() {
return "Capture Environment Variables";
}
}
}
package org.jvnet.hudson.test;
import hudson.Extension;
import hudson.model.Computer;
import hudson.model.TaskListener;
import hudson.remoting.Channel;
import hudson.remoting.VirtualChannel;
import hudson.slaves.ComputerListener;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* Runs at the end of the test to cleanup any live channels.
*
* @author Kohsuke Kawaguchi
*/
@Extension
public class ChannelShutdownListener extends ComputerListener implements EndOfTestListener {
/**
* Remember channels that are created, to release them at the end.
*/
private List<Channel> channels = new ArrayList<Channel>();
@Override
public synchronized void onOnline(Computer c, TaskListener listener) throws IOException, InterruptedException {
VirtualChannel ch = c.getChannel();
if (ch instanceof Channel) {
channels.add((Channel)ch);
}
}
@Override
public synchronized void onTearDown() throws Exception {
for (Channel c : channels)
c.close();
for (Channel c : channels)
c.join();
channels.clear();
}
}
/*
* The MIT License
*
* Copyright (c) 2004-2009, Sun Microsystems, Inc.
*
* 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 hudson.Extension;
import hudson.model.RootAction;
import org.kohsuke.stapler.QueryParameter;
import org.kohsuke.stapler.StaplerResponse;
import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
/**
* Server-side logic that implements {@link HudsonTestCase#executeOnServer(Callable)}.
*
* @author Kohsuke Kawaguchi
*/
@Extension
public final class ClosureExecuterAction implements RootAction {
private final Map<UUID,Runnable> runnables = Collections.synchronizedMap(new HashMap<UUID, Runnable>());
public void add(UUID uuid, Runnable r) {
runnables.put(uuid,r);
}
public void doIndex(StaplerResponse rsp, @QueryParameter("uuid") String uuid) throws IOException {
Runnable r = runnables.remove(UUID.fromString(uuid));
if (r!=null) {
r.run();
} else {
rsp.sendError(404);
}
}
public String getIconFileName() {
return null;
}
public String getDisplayName() {
return null;
}
public String getUrlName() {
return "closures";
}
}
/*
* The MIT License
*
* Copyright (c) 2010, InfraDNA, Inc.
*
* 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 hudson.Extension;
import hudson.model.AbstractDescribableImpl;
import hudson.model.Descriptor;
import hudson.slaves.ComputerConnector;
import hudson.slaves.ComputerConnectorDescriptor;
import org.kohsuke.stapler.StaplerRequest;
import javax.servlet.ServletException;
import java.io.IOException;
import java.util.List;
/**
* Test bed to verify the configuration roundtripness of the {@link ComputerConnector}.
*
* @author Kohsuke Kawaguchi
* @see HudsonTestCase#computerConnectorTester
*/
public class ComputerConnectorTester extends AbstractDescribableImpl<ComputerConnectorTester> {
public final HudsonTestCase testCase;
public ComputerConnector connector;
public ComputerConnectorTester(HudsonTestCase testCase) {
this.testCase = testCase;
}
public void doConfigSubmit(StaplerRequest req) throws IOException, ServletException {
connector = req.bindJSON(ComputerConnector.class, req.getSubmittedForm().getJSONObject("connector"));
}
public List getConnectorDescriptors() {
return ComputerConnectorDescriptor.all();
}
@Extension
public static class DescriptorImpl extends Descriptor<ComputerConnectorTester> {
@Override // TODO 1.635+ delete
public String getDisplayName() {
return "ComputerConnectorTester";
}
}
}
/*
* The MIT License
*
* Copyright (c) 2015 Oleg Nenashev.
*
* 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 hudson.AbortException;
import hudson.Extension;
import hudson.FilePath;
import hudson.Launcher;
import hudson.model.AbstractBuild;
import hudson.model.BuildListener;
import hudson.model.Descriptor;
import hudson.tasks.Builder;
import java.io.IOException;
import javax.annotation.Nonnull;
import net.sf.json.JSONObject;
import org.kohsuke.stapler.StaplerRequest;
/**
* Creates a test builder, which creates a file in the workspace.
* @author Oleg Nenashev
* @since TODO
*/
public class CreateFileBuilder extends Builder {
@Nonnull
private final String fileName;
@Nonnull
private final String fileContent;
public CreateFileBuilder(@Nonnull String fileName, @Nonnull String fileContent) {
this.fileName = fileName;
this.fileContent = fileContent;
}
@Nonnull
public String getFileName() {
return fileName;
}
@Nonnull
public String getFileContent() {
return fileContent;
}
@Override
public boolean perform(AbstractBuild<?, ?> build, Launcher launcher, BuildListener listener) throws InterruptedException, IOException {
listener.getLogger().println("Creating a file " + fileName);
FilePath workspace = build.getWorkspace();
if (workspace == null) {
throw new AbortException("Cannot get the workspace of the build");
}
workspace.child(fileName).write(fileContent, "UTF-8");
return true;
}
@Override
public Descriptor<Builder> getDescriptor() {
return new DescriptorImpl();
}
@Extension
public static final class DescriptorImpl extends Descriptor<Builder> {
@Override
public Builder newInstance(StaplerRequest req, JSONObject data) {
throw new UnsupportedOperationException("This is a temporarytest class, "
+ "which should not be configured from UI");
}
@Override
public String getDisplayName() {
return "Create a file";
}
}
}
package org.jvnet.hudson.test;
import junit.framework.TestCase;
/**
* Tests that the specified class has the default constructor.
*
* @author Kohsuke Kawaguchi
*/
public class DefaultConstructorChecker extends TestCase {
private final Class clazz;
public DefaultConstructorChecker(Class clazz) {
this.clazz = clazz;
setName(clazz.getName()+".verifyDefaultConstructor");
}
@Override
protected void runTest() throws Throwable {
try {
clazz.getConstructor();
} catch (NoSuchMethodException e) {
throw new Error(clazz+" must have the default constructor",e);
} catch (SecurityException e) {
throw new Error(clazz+" must have the default constructor",e);
}
}
}
package org.jvnet.hudson.test;
import hudson.ExtensionPoint;
/**
* Gets notified before the test completes to perform additional cleanup.
*
* @author Kohsuke Kawaguchi
* @since 1.520
*/
public interface EndOfTestListener extends ExtensionPoint {
/**
* Called for clean up.
*/
void onTearDown() throws Exception;
}
/*
* The MIT License
*
* Copyright (c) 2004-2009, Sun Microsystems, Inc.
*
* 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 hudson.model.AbstractBuild;
import hudson.model.User;
import hudson.scm.ChangeLogParser;
import hudson.scm.ChangeLogSet;
import org.apache.commons.digester.Digester;
import org.kohsuke.stapler.export.Exported;
import org.kohsuke.stapler.export.ExportedBean;
import org.xml.sax.SAXException;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* @author Andrew Bayer
*/
public class ExtractChangeLogParser extends ChangeLogParser {
@SuppressWarnings("rawtypes")
@Override
public ExtractChangeLogSet parse(AbstractBuild build, File changeLogFile) throws IOException, SAXException {
if (changeLogFile.exists()) {
FileInputStream fis = new FileInputStream(changeLogFile);
ExtractChangeLogSet logSet = parse(build, fis);
fis.close();
return logSet;
} else {
return new ExtractChangeLogSet(build, new ArrayList<ExtractChangeLogEntry>());
}
}
@SuppressWarnings("rawtypes")
public ExtractChangeLogSet parse(AbstractBuild build, InputStream changeLogStream) throws IOException, SAXException {
ArrayList<ExtractChangeLogEntry> changeLog = new ArrayList<ExtractChangeLogEntry>();
Digester digester = new Digester();
digester.setClassLoader(ExtractChangeLogSet.class.getClassLoader());
digester.push(changeLog);
digester.addObjectCreate("*/extractChanges/entry", ExtractChangeLogEntry.class);
digester.addBeanPropertySetter("*/extractChanges/entry/zipFile");
digester.addObjectCreate("*/extractChanges/entry/file",
FileInZip.class);
digester.addBeanPropertySetter("*/extractChanges/entry/file/fileName");
digester.addSetNext("*/extractChanges/entry/file", "addFile");
digester.addSetNext("*/extractChanges/entry", "add");
digester.parse(changeLogStream);
return new ExtractChangeLogSet(build, changeLog);
}
@ExportedBean(defaultVisibility = 999)
public static class ExtractChangeLogEntry extends ChangeLogSet.Entry {
private List<FileInZip> files = new ArrayList<FileInZip>();
private String zipFile;
public ExtractChangeLogEntry() {
}
public ExtractChangeLogEntry(String zipFile) {
this.zipFile = zipFile;
}
public void setZipFile(String zipFile) {
this.zipFile = zipFile;
}
@Exported
public String getZipFile() {
return zipFile;
}
@Override
public void setParent(ChangeLogSet parent) {
super.setParent(parent);
}
@Override
public Collection<String> getAffectedPaths() {
Collection<String> paths = new ArrayList<String>(files.size());
for (FileInZip file : files) {
paths.add(file.getFileName());
}
return paths;
}
@Override
@Exported
public User getAuthor() {
return User.get("testuser");
}
@Override
@Exported
public String getMsg() {
return "Extracted from " + zipFile;
}
public void addFile(FileInZip fileName) {
files.add(fileName);
}
public void addFiles(Collection<FileInZip> fileNames) {
this.files.addAll(fileNames);
}
}
@ExportedBean(defaultVisibility = 999)
public static class FileInZip {
private String fileName = "";
public FileInZip() {
}
public FileInZip(String fileName) {
this.fileName = fileName;
}
@Exported
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
}
}
/*
* The MIT License
*
* Copyright (c) 2004-2009, Sun Microsystems, Inc.
*
* 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 hudson.model.AbstractBuild;
import hudson.scm.ChangeLogSet;
import java.util.List;
import java.util.Collections;
import java.util.Iterator;
/**
* @author Andrew Bayer
*/
public class ExtractChangeLogSet extends ChangeLogSet<ExtractChangeLogParser.ExtractChangeLogEntry> {
private List<ExtractChangeLogParser.ExtractChangeLogEntry> changeLogs = null;
public ExtractChangeLogSet(AbstractBuild<?, ?> build, List<ExtractChangeLogParser.ExtractChangeLogEntry> changeLogs) {
super(build);
for (ExtractChangeLogParser.ExtractChangeLogEntry entry : changeLogs) {
entry.setParent(this);
}
this.changeLogs = Collections.unmodifiableList(changeLogs);
}
@Override
public boolean isEmptySet() {
return changeLogs.isEmpty();
}
public Iterator<ExtractChangeLogParser.ExtractChangeLogEntry> iterator() {
return changeLogs.iterator();
}
}
/*
* The MIT License
*
* Copyright (c) 2004-2009, Sun Microsystems, Inc.
*
* 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 hudson.FilePath;
import hudson.Launcher;
import hudson.model.AbstractBuild;
import hudson.model.BuildListener;
import hudson.scm.NullSCM;
import hudson.scm.SCM;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
/**
* {@link SCM} useful for testing that extracts the given resource as a zip file.
*
* @author Kohsuke Kawaguchi
*/
public class ExtractResourceSCM extends NullSCM {
private final URL zip;
private String parentFolder;
/**
*
* @param zip
*/
public ExtractResourceSCM(URL zip) {
if(zip==null)
throw new IllegalArgumentException();
this.zip = zip;
}
/**
* with this constructor your zip can contains a folder
* more useful to create a project test zip foo.zip foo
* @param zip
* @param parentFolder
*/
public ExtractResourceSCM(URL zip, String parentFolder) {
if(zip==null)
throw new IllegalArgumentException();
this.zip = zip;
this.parentFolder = parentFolder;
}
@Override
public boolean checkout(AbstractBuild<?,?> build, Launcher launcher, FilePath workspace, BuildListener listener, File changeLogFile) throws IOException, InterruptedException {
if (workspace.exists()) {
listener.getLogger().println("Deleting existing workspace " + workspace.getRemote());
workspace.deleteRecursive();
}
listener.getLogger().println("Staging "+zip);
workspace.unzipFrom(zip.openStream());
if (parentFolder != null) {
FileUtils.copyDirectory( new File(workspace.getRemote() + "/" + parentFolder), new File( workspace.getRemote()));
}
return true;
}
}
/*
* The MIT License
*
* Copyright (c) 2004-2009, Sun Microsystems, Inc.
*
* 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 hudson.FilePath;
import hudson.Launcher;
import hudson.Util;
import hudson.model.AbstractBuild;
import hudson.model.BuildListener;
import hudson.scm.ChangeLogParser;
import hudson.scm.NullSCM;
import hudson.scm.SCM;
import hudson.scm.SCMDescriptor;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.net.URL;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
/**
* {@link SCM} useful for testing that extracts the given resource as a zip file.
*
* @author Kohsuke Kawaguchi
*/
public class ExtractResourceWithChangesSCM extends NullSCM {
private final URL firstZip;
private final URL secondZip;
private final String moduleRoot;
public ExtractResourceWithChangesSCM(URL firstZip, URL secondZip) {
if ((firstZip == null) || (secondZip == null))
throw new IllegalArgumentException();
this.firstZip = firstZip;
this.secondZip = secondZip;
this.moduleRoot = null;
}
public ExtractResourceWithChangesSCM(URL firstZip, URL secondZip, String moduleRoot) {
if ((firstZip == null) || (secondZip == null))
throw new IllegalArgumentException();
this.firstZip = firstZip;
this.secondZip = secondZip;
this.moduleRoot = moduleRoot;
}
@Override
public FilePath getModuleRoot(FilePath workspace) {
if (moduleRoot!=null) {
return workspace.child(moduleRoot);
}
return workspace;
}
@Override
public boolean checkout(AbstractBuild<?,?> build, Launcher launcher, FilePath workspace, BuildListener listener, File changeLogFile) throws IOException, InterruptedException {
if (workspace.exists()) {
listener.getLogger().println("Deleting existing workspace " + workspace.getRemote());
workspace.deleteRecursive();
}
listener.getLogger().println("Staging first zip: " + firstZip);
workspace.unzipFrom(firstZip.openStream());
listener.getLogger().println("Staging second zip: " + secondZip);
workspace.unzipFrom(secondZip.openStream());
// Get list of files changed in secondZip.
ZipInputStream zip = new ZipInputStream(secondZip.openStream());
ZipEntry e;
ExtractChangeLogParser.ExtractChangeLogEntry changeLog = new ExtractChangeLogParser.ExtractChangeLogEntry(secondZip.toString());
try {
while ((e = zip.getNextEntry()) != null) {
if (!e.isDirectory())
changeLog.addFile(new ExtractChangeLogParser.FileInZip(e.getName()));
}
}
finally {
zip.close();
}
saveToChangeLog(changeLogFile, changeLog);
return true;
}
@Override
public ChangeLogParser createChangeLogParser() {
return new ExtractChangeLogParser();
}
private static String escapeForXml(String string) {
return Util.xmlEscape(Util.fixNull(string));
}
public void saveToChangeLog(File changeLogFile, ExtractChangeLogParser.ExtractChangeLogEntry changeLog) throws IOException {
FileOutputStream outputStream = new FileOutputStream(changeLogFile);
PrintStream stream = new PrintStream(outputStream, false, "UTF-8");
stream.println("<?xml version='1.0' encoding='UTF-8'?>");
stream.println("<extractChanges>");
stream.println("<entry>");
stream.println("<zipFile>" + escapeForXml(changeLog.getZipFile()) + "</zipFile>");
for (String fileName : changeLog.getAffectedPaths()) {
stream.println("<file>");
stream.println("<fileName>" + escapeForXml(fileName) + "</fileName>");
stream.println("</file>");
}
stream.println("</entry>");
stream.println("</extractChanges>");
stream.close();
}
/**
* Don't write 'this', so that subtypes can be implemented as anonymous class.
*/
private Object writeReplace() { return new Object(); }
@Override public SCMDescriptor<?> getDescriptor() {
return new SCMDescriptor<ExtractResourceWithChangesSCM>(ExtractResourceWithChangesSCM.class, null) {
@Override // TODO 1.635+ delete
public String getDisplayName() {
return "ExtractResourceWithChangesSCM";
}
};
}
}
/*
* The MIT License
*
* Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi
*
* 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 hudson.Extension;
import hudson.model.Descriptor;
import hudson.model.Result;
import hudson.tasks.Builder;
import net.sf.json.JSONObject;
import org.kohsuke.stapler.StaplerRequest;
/**
* Mock {@link Builder} that always cause a build to fail.
*
* @author Kohsuke Kawaguchi
*/
public class FailureBuilder extends MockBuilder {
public FailureBuilder() {
super(Result.FAILURE);
}
@Extension
public static final class DescriptorImpl extends Descriptor<Builder> {
public FailureBuilder newInstance(StaplerRequest req, JSONObject data) {
return new FailureBuilder();
}
@Override // TODO 1.635+ delete
public String getDisplayName() {
return "FailureBuilder";
}
}
}
/*
* The MIT License
*
* Copyright (c) 2011, CloudBees, Inc.
*
* 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 hudson.FilePath;
import hudson.Launcher;
import hudson.model.AbstractBuild;
import hudson.model.BuildListener;
import hudson.model.InvisibleAction;
import hudson.model.User;
import hudson.scm.ChangeLogParser;
import hudson.scm.ChangeLogSet;
import hudson.scm.ChangeLogSet.Entry;
import hudson.scm.NullSCM;
import hudson.scm.SCM;
import hudson.scm.SCMDescriptor;
import org.xml.sax.SAXException;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
/**
* Fake SCM implementation that can report arbitrary commits from arbitrary users.
*
* @author Kohsuke Kawaguchi
*/
public class FakeChangeLogSCM extends NullSCM {
/**
* Changes to be reported in the next build.
*/
private List<EntryImpl> entries = new ArrayList<EntryImpl>();
public EntryImpl addChange() {
EntryImpl e = new EntryImpl();
entries.add(e);
return e;
}
@Override
public boolean checkout(AbstractBuild<?, ?> build, Launcher launcher, FilePath remoteDir, BuildListener listener, File changeLogFile) throws IOException, InterruptedException {
new FilePath(changeLogFile).touch(0);
build.addAction(new ChangelogAction(entries));
entries = new ArrayList<EntryImpl>();
return true;
}
@Override
public ChangeLogParser createChangeLogParser() {
return new FakeChangeLogParser();
}
@Override public SCMDescriptor<?> getDescriptor() {
return new SCMDescriptor<SCM>(null) {
@Override // TODO 1.635+ delete
public String getDisplayName() {
return "FakeChangeLogSCM";
}
};
}
public static class ChangelogAction extends InvisibleAction {
private final List<EntryImpl> entries;
public ChangelogAction(List<EntryImpl> entries) {
this.entries = entries;
}
}
public static class FakeChangeLogParser extends ChangeLogParser {
@SuppressWarnings("rawtypes")
@Override
public FakeChangeLogSet parse(AbstractBuild build, File changelogFile) throws IOException, SAXException {
return new FakeChangeLogSet(build, build.getAction(ChangelogAction.class).entries);
}
}
public static class FakeChangeLogSet extends ChangeLogSet<EntryImpl> {
private List<EntryImpl> entries;
public FakeChangeLogSet(AbstractBuild<?, ?> build, List<EntryImpl> entries) {
super(build);
this.entries = entries;
}
@Override
public boolean isEmptySet() {
return entries.isEmpty();
}
public Iterator<EntryImpl> iterator() {
return entries.iterator();
}
}
public static class EntryImpl extends Entry {
private String msg = "some commit message";
private String author = "someone";
public EntryImpl withAuthor(String author) {
this.author = author;
return this;
}
public EntryImpl withMsg(String msg) {
this.msg = msg;
return this;
}
@Override
public String getMsg() {
return msg;
}
@Override
public User getAuthor() {
return User.get(author);
}
@Override
public Collection<String> getAffectedPaths() {
return Collections.singleton("path");
}
}
}
package org.jvnet.hudson.test;
import hudson.Launcher.ProcStarter;
import hudson.Proc;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
* Fake a process launch.
*
* @author Kohsuke Kawaguchi
* @see PretendSlave
*/
public interface FakeLauncher {
/**
* Called whenever a {@link PretendSlave} is asked to fork a new process.
*
* <p>
* The callee can inspect the {@link ProcStarter} object to determine what process is being launched,
* and if necessary, fake a process launch by either returning a valid {@link Proc} object, or let
* the normal process launch happen by returning null.
*/
Proc onLaunch(ProcStarter p) throws IOException;
/**
* Fake {@link Proc} implementation that represents a completed process.
*/
class FinishedProc extends Proc {
public final int exitCode;
public FinishedProc(int exitCode) {
this.exitCode = exitCode;
}
@Override
public boolean isAlive() throws IOException, InterruptedException {
return false;
}
@Override
public void kill() throws IOException, InterruptedException {
throw new UnsupportedOperationException();
}
@Override
public int join() throws IOException, InterruptedException {
return exitCode;
}
@Override
public InputStream getStdout() {
// TODO
throw new UnsupportedOperationException();
}
@Override
public InputStream getStderr() {
// TODO
throw new UnsupportedOperationException();
}
@Override
public OutputStream getStdin() {
// TODO
throw new UnsupportedOperationException();
}
}
}
package org.jvnet.hudson.test;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;
import groovy.lang.Closure;
import hudson.model.AbstractBuild;
import hudson.model.BuildListener;
import hudson.tasks.Builder;
import hudson.Launcher;
import java.util.concurrent.Callable;
import java.io.IOException;
/**
* {@link HudsonTestCase} with more convenience methods for Groovy.
*
* @author Kohsuke Kawaguchi
* @deprecated Use {@link GroovyJenkinsRule} instead.
*/
@Deprecated
public abstract class GroovyHudsonTestCase extends HudsonTestCase {
/**
* Executes the given closure on the server, in the context of an HTTP request.
* This is useful for testing some methods that require {@link StaplerRequest} and {@link StaplerResponse}.
*
* <p>
* The closure will get the request and response as parameters.
*/
public Object executeOnServer(final Closure c) throws Exception {
return executeOnServer(new Callable<Object>() {
public Object call() throws Exception {
return c.call();
}
});
}
/**
* Wraps a closure as a {@link Builder}.
*/
public Builder builder(final Closure c) {
return new TestBuilder() {
public boolean perform(AbstractBuild<?,?> build, Launcher launcher, BuildListener listener) throws InterruptedException, IOException {
Object r = c.call(new Object[]{build,launcher,listener});
if (r instanceof Boolean) return (Boolean)r;
return true;
}
};
}
}
/*
* The MIT License
*
* Copyright 2013 Jesse Glick.
*
* 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 groovy.lang.Closure;
import hudson.Launcher;
import hudson.model.AbstractBuild;
import hudson.model.BuildListener;
import hudson.tasks.Builder;
import java.io.IOException;
import java.util.concurrent.Callable;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;
/**
* {@link JenkinsRule} variant with special options for tests written in Groovy.
* @since 1.535
*/
public class GroovyJenkinsRule extends JenkinsRule {
/**
* Executes the given closure on the server, in the context of an HTTP request.
* This is useful for testing some methods that require {@link StaplerRequest} and {@link StaplerResponse}.
*
* <p>
* The closure will get the request and response as parameters.
*/
public Object executeOnServer(final Closure c) throws Exception {
return executeOnServer(new Callable<Object>() {
@Override public Object call() throws Exception {
return c.call();
}
});
}
/**
* Wraps a closure as a {@link Builder}.
*/
public Builder builder(final Closure c) {
return new TestBuilder() {
@Override public boolean perform(AbstractBuild<?,?> build, Launcher launcher, BuildListener listener) throws InterruptedException, IOException {
Object r = c.call(new Object[] {build, launcher, listener});
if (r instanceof Boolean) {
return (Boolean) r;
}
return true;
}
};
}
}
/*
* The MIT License
*
* Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi
*
* 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 hudson.FilePath;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
/**
* Controls how a {@link HudsonTestCase} initializes <tt>JENKINS_HOME</tt>.
*
* @author Kohsuke Kawaguchi
*/
public interface HudsonHomeLoader {
/**
* Returns a directory to be used as <tt>JENKINS_HOME</tt>
*
* @throws Exception
* to cause a test to fail.
*/
File allocate() throws Exception;
/**
* Allocates a new empty directory, meaning this will emulate the fresh Hudson installation.
*/
HudsonHomeLoader NEW = new HudsonHomeLoader() {
public File allocate() throws IOException {
return TestEnvironment.get().temporaryDirectoryAllocator.allocate();
}
};
/**
* Allocates a new directory by copying from an existing directory, or unzipping from a zip file.
*/
final class CopyExisting implements HudsonHomeLoader {
private final URL source;
/**
* Either a zip file or a directory that contains the home image.
*/
public CopyExisting(File source) throws MalformedURLException {
this(source.toURI().toURL());
}
/**
* Extracts from a zip file in the resource.
*
* <p>
* This is useful in case you want to have a test data in the resources.
* Only file URL is supported.
*/
public CopyExisting(URL source) {
this.source = source;
}
public File allocate() throws Exception {
File target = NEW.allocate();
if(source.getProtocol().equals("file")) {
File src = new File(source.toURI());
if(src.isDirectory())
new FilePath(src).copyRecursiveTo("**/*",new FilePath(target));
else
if(src.getName().endsWith(".zip"))
new FilePath(src).unzip(new FilePath(target));
} else {
File tmp = File.createTempFile("hudson","zip");
try {
FileUtils.copyURLToFile(source,tmp);
new FilePath(tmp).unzip(new FilePath(target));
} finally {
tmp.delete();
}
}
return target;
}
}
/**
* Allocates a new directory by copying from a test resource
*/
final class Local implements HudsonHomeLoader {
private final Method testMethod;
public Local(Method testMethod) {
this.testMethod = testMethod;
}
public File allocate() throws Exception {
URL res = findDataResource();
if(!res.getProtocol().equals("file"))
throw new AssertionError("Test data is not available in the file system: "+res);
// if we picked up a directory, it's one level above config.xml
File home = new File(res.toURI());
if(!home.getName().endsWith(".zip"))
home = home.getParentFile();
return new CopyExisting(home).allocate();
}
private URL findDataResource() {
// first, check method specific resource
Class<?> clazz = testMethod.getDeclaringClass();
for( String middle : new String[]{ '/'+testMethod.getName(), "" }) {
for( String suffix : SUFFIXES ) {
URL res = clazz.getResource(clazz.getSimpleName() + middle+suffix);
if(res!=null) return res;
}
}
throw new AssertionError("No test resource was found for "+testMethod);
}
private static final String[] SUFFIXES = {"/config.xml",".zip"};
}
}
/*
* The MIT License
*
* Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi
*
* 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.DefaultPageCreator;
import com.gargoylesoftware.htmlunit.Page;
import com.gargoylesoftware.htmlunit.WebResponse;
import com.gargoylesoftware.htmlunit.WebWindow;
import com.gargoylesoftware.htmlunit.PageCreator;
import org.apache.commons.lang3.StringUtils;
import java.io.IOException;
import java.io.InputStream;
import java.util.Locale;
/**
* {@link PageCreator} that understands JNLP file.
*
* @author Kohsuke Kawaguchi
*/
public class HudsonPageCreator extends DefaultPageCreator {
@Override
public Page createPage(WebResponse webResponse, WebWindow webWindow) throws IOException {
String contentType = webResponse.getContentType().toLowerCase(Locale.ENGLISH);
if(contentType.equals("application/x-java-jnlp-file"))
return createXmlPage(webResponse, webWindow);
return super.createPage(webResponse, webWindow);
}
@Override
protected String determineContentType(String contentType, InputStream contentAsStream) throws IOException {
// Need to sidestep HtmlUnit default behaviour here. It defaults the response type to
// being text/plain (and so creates a TextPage) if the content type in the response is
// blank + is an empty response.
if (StringUtils.isEmpty(contentType)) {
return "text/html";
}
return super.determineContentType(contentType, contentAsStream);
}
public static final HudsonPageCreator INSTANCE = new HudsonPageCreator();
}
package org.jvnet.hudson.test;
import hudson.Util;
import hudson.util.IOUtils;
import org.apache.commons.io.FileUtils;
import org.mortbay.jetty.Server;
import org.mortbay.jetty.bio.SocketConnector;
import org.mortbay.jetty.handler.ContextHandlerCollection;
import org.mortbay.jetty.servlet.Context;
import org.mortbay.jetty.servlet.ServletHolder;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.net.URL;
/**
* Acts as a reverse proxy, so that during a test we can avoid hitting updates.jenkins-ci.org.
*
* <p>
* The contents are cached locally.
*
* @author Kohsuke Kawaguchi
*/
public class JavaNetReverseProxy extends HttpServlet {
private final Server server;
public final int localPort;
private final File cacheFolder;
public JavaNetReverseProxy(File cacheFolder) throws Exception {
this.cacheFolder = cacheFolder;
cacheFolder.mkdirs();
server = new Server();
ContextHandlerCollection contexts = new ContextHandlerCollection();
server.setHandler(contexts);
Context root = new Context(contexts, "/", Context.SESSIONS);
root.addServlet(new ServletHolder(this), "/");
SocketConnector connector = new SocketConnector();
server.addConnector(connector);
server.start();
localPort = connector.getLocalPort();
}
public void stop() throws Exception {
server.stop();
}
// class Response {
// final URL url;
// final String contentType;
// final ByteArrayOutputStream data = new ByteArrayOutputStream();
//
// Response(URL url) throws IOException {
// this.url = url;
// URLConnection con = url.openConnection();
// contentType = con.getContentType();
// IOUtils.copy(con.getInputStream(),data);
// }
//
// void reproduceTo(HttpServletResponse rsp) throws IOException {
// rsp.setContentType(contentType);
// data.writeTo(rsp.getOutputStream());
// }
// }
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String path = req.getServletPath();
String d = Util.getDigestOf(path);
File cache = new File(cacheFolder, d);
if(!cache.exists()) {
URL url = new URL("http://updates.jenkins-ci.org/" + path);
FileUtils.copyURLToFile(url,cache);
}
resp.setContentType(getMimeType(path));
IOUtils.copy(cache,resp.getOutputStream());
}
private String getMimeType(String path) {
int idx = path.indexOf('?');
if(idx>=0)
path = path.substring(0,idx);
if(path.endsWith(".json")) return "text/javascript";
return getServletContext().getMimeType(path);
}
private static volatile JavaNetReverseProxy INSTANCE;
/**
* Gets the default instance.
*/
public static synchronized JavaNetReverseProxy getInstance() throws Exception {
if(INSTANCE==null)
// TODO: think of a better location --- ideally inside the target/ dir so that clean would wipe them out
INSTANCE = new JavaNetReverseProxy(new File(new File(System.getProperty("java.io.tmpdir")),"jenkins-ci.org-cache2"));
return INSTANCE;
}
}
/*
* The MIT License
*
* Copyright (c) 2010, InfraDNA, Inc.
*
* 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 hudson.Extension;
import hudson.model.AbstractDescribableImpl;
import hudson.model.Descriptor;
import hudson.slaves.ComputerConnector;
import hudson.slaves.ComputerConnectorDescriptor;
import org.kohsuke.stapler.StaplerRequest;
import javax.servlet.ServletException;
import java.io.IOException;
import java.util.List;
/**
* Test bed to verify the configuration roundtripness of the {@link ComputerConnector}.
*
* @author Kohsuke Kawaguchi
* @see HudsonTestCase#computerConnectorTester
* @since 1.436
*/
public class JenkinsComputerConnectorTester extends AbstractDescribableImpl<JenkinsComputerConnectorTester> {
public final JenkinsRule jenkinsRule;
public ComputerConnector connector;
public JenkinsComputerConnectorTester(JenkinsRule testCase) {
this.jenkinsRule = testCase;
}
public void doConfigSubmit(StaplerRequest req) throws IOException, ServletException {
connector = req.bindJSON(ComputerConnector.class, req.getSubmittedForm().getJSONObject("connector"));
}
public List getConnectorDescriptors() {
return ComputerConnectorDescriptor.all();
}
@Extension
public static class DescriptorImpl extends Descriptor<JenkinsComputerConnectorTester> {
@Override // TODO 1.635+ delete
public String getDisplayName() {
return "JenkinsComputerConnectorTester";
}
}
}
/*
* The MIT License
*
* Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi
*
* 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;
/**
* Like {@link Runnable} but can throw any exception.
*
* @author Kohsuke Kawaguchi
*/
public interface LenientRunnable {
void run() throws Exception;
}
package org.jvnet.hudson.test;
import org.jvnet.hudson.test.recipes.Recipe;
import org.jvnet.hudson.test.recipes.WithPlugin;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* An annotation for test methods that do not require the {@link JenkinsRule}/{@link HudsonTestCase} to create and tear down the jenkins
* instance.
*/
@Retention(RUNTIME)
@Documented
@Target(METHOD)
@Recipe(WithoutJenkins.RunnerImpl.class)
public @interface WithoutJenkins {
class RunnerImpl extends Recipe.Runner<WithPlugin> {
// bogus. this recipe is handled differently by HudsonTestCase
}
}
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册