Class RealJenkinsRule

java.lang.Object
org.jvnet.hudson.test.RealJenkinsRule
All Implemented Interfaces:
org.junit.rules.TestRule

public final class RealJenkinsRule extends Object implements org.junit.rules.TestRule
Like JenkinsSessionRule but running Jenkins in a more realistic environment.

Though Jenkins is run in a separate JVM using Winstone (java -jar jenkins.war), you can still do “whitebox” testing: directly calling Java API methods, starting from JenkinsRule or not. This is because the test code gets sent to the remote JVM and loaded and run there. (Thus when using Maven, there are at least three JVMs involved: Maven itself; the Surefire booter with your top-level test code; and the Jenkins controller with test bodies.) Just as with JenkinsRule, all plugins found in the test classpath will be enabled, but with more realistic behavior: class loaders in a graph, pluginFirstClassLoader and maskClasses, etc.

“Compile-on-save” style development works for classes and resources in the current plugin: with a suitable IDE, you can edit a source file, have it be sent to target/classes/, and rerun a test without needing to go through a full Maven build cycle. This is because target/test-classes/the.hpl is used to load unpacked plugin resources.

Like JenkinsRule, the controller is started in “development mode”: the setup wizard is suppressed, the update center is not checked, etc.

Known limitations:

  • Execution is a bit slower due to the overhead of launching a new JVM; and class loading overhead cannot be shared between test cases. More memory is needed.
  • Remote thunks must be serializable. If they need data from the test JVM, you will need to create a static nested class to package that.
  • static state cannot be shared between the top-level test code and test bodies (though the compiler will not catch this mistake).
  • When using a snapshot dep on Jenkins core, you must build jenkins.war to test core changes (there is no “compile-on-save” support for this).
  • TestExtension is not available.
  • LoggerRule is not available, however additional loggers can be configured via withLogger(Class, Level)}.
  • BuildWatcher is not available, but you can use TailLog instead.

Systems not yet tested:

  • Possibly Timeout can be used.
  • Constructor Details

  • Method Details

    • addPlugins

      public RealJenkinsRule addPlugins(String... plugins)
      Add some plugins to the test classpath.
      Parameters:
      plugins - Filenames of the plugins to install. These are expected to be absolute test classpath resources, such as plugins/workflow-job.hpi for example.

      For small fake plugins built for this purpose and exercising some bit of code, use addSyntheticPlugin(java.lang.Package). If you wish to test with larger archives of real plugins, this is possible for example by binding dependency:copy to the process-test-resources phase.

      In most cases you do not need this method. Simply add whatever plugins you are interested in testing against to your POM in test scope. These, and their transitive dependencies, will be loaded in all RealJenkinsRule tests. This method is useful if only a particular test may load the tested plugin, or if the tested plugin is not available in a repository for use as a test dependency.

    • addSyntheticPlugin

      public RealJenkinsRule.SyntheticPlugin addSyntheticPlugin(Package pkg)
      Adds a test-only plugin to the controller based on sources defined in this module. Useful when you wish to define some types, register some Extensions, etc. and there is no existing plugin that does quite what you want (that you are comfortable adding to the test classpath and maintaining the version of).

      If you also have some test suites based on JenkinsRule, you may not want to use Extension since (unlike TestExtension) it would be loaded in all such tests. Instead create a package-info.java specifying an @OptionalPackage whose requirePlugins lists the same RealJenkinsRule.SyntheticPlugin.shortName(String). (You will need to .header("Plugin-Dependencies", "variant:0") to use this API.) Then use @OptionalExtension on all your test extensions. These will then be loaded only in RealJenkinsRule-based tests requesting this plugin.

      Parameters:
      pkg - the Java package containing any classes and resources you want included
      Returns:
      a builder
    • omitPlugins

      public RealJenkinsRule omitPlugins(String... plugins)
      Omit some plugins in the test classpath.
      Parameters:
      plugins - one or more code names, like token-macro
    • javaOptions

      public RealJenkinsRule javaOptions(String... options)
      Add some JVM startup options.
      Parameters:
      options - one or more options, like -Dorg.jenkinsci.Something.FLAG=true
    • jenkinsOptions

      public RealJenkinsRule jenkinsOptions(String... options)
      Add some Jenkins (including Winstone) startup options. You probably meant to use javaOptions(String...).
      Parameters:
      options - one or more options, like --webroot=/tmp/war --pluginroot=/tmp/plugins
    • extraEnv

      public RealJenkinsRule extraEnv(String key, String value)
      Set an extra environment variable.
      Parameters:
      value - null to cancel a previously set variable
    • withTimeout

      public RealJenkinsRule withTimeout(int timeout)
      Adjusts the test timeout. The timer starts when startJenkins() completes and runRemotely(org.jvnet.hudson.test.RealJenkinsRule.Step...) is ready. The default is currently set to 600 (10m).
      Parameters:
      timeout - number of seconds before exiting, or zero to disable
    • withHost

      public RealJenkinsRule withHost(String host)
      Sets a custom host name for the Jenkins root URL.

      By default, this is just localhost. But you may wish to set it to something else that resolves to localhost, such as some-id.localtest.me. This is particularly useful when running multiple copies of Jenkins (and/or other services) in one test case, since browser cookies are sensitive to host but not port and so otherwise HttpServletRequest.getSession(boolean) might accidentally be shared across otherwise distinct services.

      Calling this method does not change the fact that Jenkins will be configured to listen only on localhost for security reasons (so others in the same network cannot access your system under test, especially if it lacks authentication).

      When using HTTPS, use https(String,KeyStoreManager, X509Certificate) instead.

    • withWar

      public RealJenkinsRule withWar(File war)
      Sets a custom WAR file to be used by the rule instead of the one in the path or war/target/jenkins.war in case of core.
    • withJavaHome

      public RealJenkinsRule withJavaHome(String JavaHome)
      Allows to specify a java home, defaults to JAVA_HOME if not used
    • withLogger

      public RealJenkinsRule withLogger(Class<?> clazz, Level level)
    • withPackageLogger

      public RealJenkinsRule withPackageLogger(Class<?> clazz, Level level)
    • withLogger

      public RealJenkinsRule withLogger(String logger, Level level)
    • withName

      public RealJenkinsRule withName(String name)
      Sets a name for this instance, which will be prefixed to log messages to simplify debugging.
    • getName

      public String getName()
    • withColor

      Applies ANSI coloration to log lines produced by this instance, complementing withName(java.lang.String). Ignored when on CI.
    • withPort

      public RealJenkinsRule withPort(int port)
      Provides a custom fixed port instead of a random one.
      Parameters:
      port - a custom port to use instead of a random one.
    • withHttpListenAddress

      public RealJenkinsRule withHttpListenAddress(String httpListenAddress)
      Provides a custom interface to listen to.

      Important: for security reasons this should be overridden only in special scenarios, such as testing inside a Docker container. Otherwise a developer running tests could inadvertently expose a Jenkins service without password protection, allowing remote code execution.

      Parameters:
      httpListenAddress - network interface such as
      0.0.0.0
      . Defaults to
      127.0.0.1
      .
    • withDebugPort

      public RealJenkinsRule withDebugPort(int debugPort)
      Allows usage of a static debug port instead of a random one.

      This allows to use predefined debug configurations in the IDE.

      Typical usage is in a base test class where multiple named controller instances are defined with fixed ports

       public RealJenkinsRule cc1 = new RealJenkinsRule().withName("cc1").withDebugPort(4001).withDebugServer(false);
      
       public RealJenkinsRule cc2 = new RealJenkinsRule().withName("cc2").withDebugPort(4002).withDebugServer(false);
       
      Then have debug configurations in the IDE set for ports
      • 5005 (test VM) - debugger mode "attach to remote vm"
      • 4001 (cc1) - debugger mode "listen to remote vm"
      • 4002 (cc2) - debugger mode "listen to remote vm"

      This allows for debugger to reconnect in scenarios where restarts of controllers are involved.

      Parameters:
      debugPort - the TCP port to use for debugging this Jenkins instance. Between 0 (random) and 65536 (excluded).
    • withDebugServer

      public RealJenkinsRule withDebugServer(boolean debugServer)
      Allows to use debug in server mode or client mode. Client mode is friendlier to controller restarts.
      Parameters:
      debugServer - true to use server=y, false to use server=n
      See Also:
    • withDebugSuspend

      public RealJenkinsRule withDebugSuspend(boolean debugSuspend)
      Whether to suspend the controller VM on startup until debugger is connected. Defaults to false.
      Parameters:
      debugSuspend - true to suspend the controller VM on startup until debugger is connected.
    • includeTestClasspathPlugins

      public RealJenkinsRule includeTestClasspathPlugins(boolean includeTestClasspathPlugins)
      The intended use case for this is to use the plugins bundled into the war withWar(File) instead of the plugins in the pom. A typical scenario for this feature is a test which does not live inside a plugin's src/test/java
      Parameters:
      includeTestClasspathPlugins - false if plugins from pom should not be used (default true)
    • prepareHomeLazily

      public RealJenkinsRule prepareHomeLazily(boolean prepareHomeLazily)
      Allows JENKINS_HOME initialization to be delayed until startJenkins() is called for the first time.

      This allows methods such as addPlugins(java.lang.String...) to be called dynamically inside of test methods, which enables related tests that need to configure RealJenkinsRule in different ways to be defined in the same class using only a single instance of RealJenkinsRule.

    • withFIPSEnabled

      public RealJenkinsRule withFIPSEnabled()
      Use withFIPSEnabled(FIPSTestBundleProvider) with default value of FIPSTestBundleProvider.get()
    • withFIPSEnabled

      public RealJenkinsRule withFIPSEnabled(io.jenkins.test.fips.FIPSTestBundleProvider fipsTestBundleProvider)
      +
      Parameters:
      fipsTestBundleProvider - the FIPSTestBundleProvider to use for testing
    • withBootClasspath

      public RealJenkinsRule withBootClasspath(File... files)
      Parameters:
      files - add some File to bootclasspath
    • getJacocoAgentOptions

      public static List<String> getJacocoAgentOptions()
    • apply

      public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description)
      Specified by:
      apply in interface org.junit.rules.TestRule
    • deprovision

      public void deprovision() throws Exception
      Deletes JENKINS_HOME. This method does not need to be invoked when using @Rule or @ClassRule to run RealJenkinsRule.
      Throws:
      Exception
    • createTempDirectory

      public Path createTempDirectory(String prefix) throws IOException
      Creates a temporary directory. Unlike Files.createTempDirectory(String, FileAttribute...) this will be cleaned up after the test exits (like TemporaryFolder), and will honor java.io.tmpdir set by Surefire after StaticProperty.JAVA_IO_TMPDIR has been initialized.
      Throws:
      IOException
    • isAlive

      public boolean isAlive()
      Returns true if the Jenkins process is alive.
    • getTruststoreJavaOptions

      public String[] getTruststoreJavaOptions()
    • then

      public void then(RealJenkinsRule.Step... steps) throws Throwable
      Run one Jenkins session, send one or more test thunks, and shut down.
      Throws:
      Throwable
    • then

      public <T extends Serializable> T then(RealJenkinsRule.Step2<T> s) throws Throwable
      Run one Jenkins session, send a test thunk, and shut down.
      Throws:
      Throwable
    • getUrl

      public URL getUrl() throws MalformedURLException
      Similar to JenkinsRule.getURL(). Requires Jenkins to be started before using startJenkins().
      Throws:
      MalformedURLException
    • https

      public RealJenkinsRule https()
      Sets up HTTPS for the current instance, and disables plain HTTP. This generates a self-signed certificate for localhost. The corresponding root CA that needs to be trusted by HTTP client can be obtained using getRootCA().
      Returns:
      the current instance
      See Also:
    • https

      public RealJenkinsRule https(@NonNull String host, @NonNull KeyStoreManager keyStoreManager, @NonNull X509Certificate rootCA)
      Sets up HTTPS for the current instance, and disables plain HTTP.

      You don't need to call withHost(String) when calling this method.

      Parameters:
      host - the host name to use in the certificate
      keyStoreManager - a key store manager containing the key and certificate to use for HTTPS. It needs to be valid for the given host
      rootCA - the certificate that needs to be trusted by callers.
      Returns:
      the current instance
      See Also:
    • getRootCA

      @Nullable public X509Certificate getRootCA()
      Returns:
      the current autogenerated root CA or null if https() has not been called.
    • buildSSLContext

      @NonNull public SSLContext buildSSLContext() throws NoSuchAlgorithmException
      Builds a SSLContext trusting the current instance.
      Throws:
      NoSuchAlgorithmException
    • getHome

      public File getHome()
      Obtains the Jenkins home directory. Normally it will suffice to use LocalData to populate files.
    • setHome

      public void setHome(File newHome)
      Switch the Jenkins home directory. Will affect subsequent startups of this rule, but not other copies linked via RealJenkinsRule(RealJenkinsRule). Normally unnecessary but could be used to simulate running on the wrong home.
    • createWebClient

      public org.htmlunit.WebClient createWebClient()
      Create a client configured to trust any self-signed certificate used by this instance.
    • startJenkins

      public void startJenkins() throws Throwable
      Throws:
      Throwable
    • checkResult

      @CheckForNull public static String checkResult(HttpURLConnection conn) throws IOException
      Throws:
      IOException
    • stopJenkins

      public void stopJenkins() throws Throwable
      Stops Jenkins and releases any system resources associated with it. If Jenkins is already stopped then invoking this method has no effect.
      Throws:
      Throwable
    • runRemotely

      public void runRemotely(RealJenkinsRule.Step... steps) throws Throwable
      Runs one or more steps on the remote system. (Compared to multiple calls, passing a series of steps is slightly more efficient as only one network call is made.)
      Throws:
      Throwable
    • runRemotely

      public <T extends Serializable> T runRemotely(RealJenkinsRule.Step2<T> s) throws Throwable
      Throws:
      Throwable
    • runRemotely

      public <A1 extends Serializable> void runRemotely(RealJenkinsRule.StepWithOneArg<A1> s, A1 arg1) throws Throwable
      Throws:
      Throwable
    • runRemotely

      public <A1 extends Serializable, A2 extends Serializable> void runRemotely(RealJenkinsRule.StepWithTwoArgs<A1,A2> s, A1 arg1, A2 arg2) throws Throwable
      Throws:
      Throwable
    • runRemotely

      public <A1 extends Serializable, A2 extends Serializable, A3 extends Serializable> void runRemotely(RealJenkinsRule.StepWithThreeArgs<A1,A2,A3> s, A1 arg1, A2 arg2, A3 arg3) throws Throwable
      Throws:
      Throwable
    • runRemotely

      public <A1 extends Serializable, A2 extends Serializable, A3 extends Serializable, A4 extends Serializable> void runRemotely(RealJenkinsRule.StepWithFourArgs<A1,A2,A3,A4> s, A1 arg1, A2 arg2, A3 arg3, A4 arg4) throws Throwable
      Throws:
      Throwable
    • runRemotely

      public <R extends Serializable, A1 extends Serializable> R runRemotely(RealJenkinsRule.StepWithReturnAndOneArg<R,A1> s, A1 arg1) throws Throwable
      Throws:
      Throwable
    • runRemotely

      public <R extends Serializable, A1 extends Serializable, A2 extends Serializable> R runRemotely(RealJenkinsRule.StepWithReturnAndTwoArgs<R,A1,A2> s, A1 arg1, A2 arg2) throws Throwable
      Throws:
      Throwable
    • runRemotely

      public <R extends Serializable, A1 extends Serializable, A2 extends Serializable, A3 extends Serializable> R runRemotely(RealJenkinsRule.StepWithReturnAndThreeArgs<R,A1,A2,A3> s, A1 arg1, A2 arg2, A3 arg3) throws Throwable
      Throws:
      Throwable
    • runRemotely

      public <R extends Serializable, A1 extends Serializable, A2 extends Serializable, A3 extends Serializable, A4 extends Serializable> R runRemotely(RealJenkinsRule.StepWithReturnAndFourArgs<R,A1,A2,A3,A4> s, A1 arg1, A2 arg2, A3 arg3, A4 arg4) throws Throwable
      Throws:
      Throwable