Multi-project build to test a Gradle plugin

I have a multi-project build… perhaps it’s slightly different than other use cases. The main build is for a Gradle plugin for building the content for a proprietary Analytics tool. The subproject is a build that actually uses the plugin… it’s the best way I can find to appropriately test the plugin.

In the past, I’ve done the two builds using separate Git repositories with the CI server executing both builds as CI job dependencies. I’d like to put them together in a single build with a single Git repository, as I always want to test the plugin in a real build as a step in building the plugin itself.

The main build is Groovy, and uninteresting (reduced for brevity):

apply plugin: 'groovy'
apply plugin: 'idea'

sourceCompatibility = 1.7
targetCompatibility = 1.7

buildscript {
    repositories {
        jcenter()
    }
}

The sub-project has it’s own build.gradle file, and in it is the mention of the plugin (reduced for brevity):

buildscript {

    repositories {
        jcenter()
    }
   }

apply plugin: 'com.redpillanalytics.checkmate.obi'

I get the following error:

* What went wrong:
A problem occurred evaluating project ':obiee/sugarcrm'.
> Plugin with id 'com.redpillanalytics.checkmate.obi' not found.

This makes perfect sense, as the plugin hasn’t been built yet. The previous build was called with “–configure-on-demand”, but the projects are coupled (seemingly) because the main build script is configuring both projects when called.

My settings.gradle file looks like the following:

rootProject.name = 'checkmate'
include 'obiee/edw'

Is this possible?

I think what you want to do is keep it as a single project (the plugin) and add a second level of tests (integTest). As part of the integration tests, you’ll have an example project to try it on.

@bmuschko has a lot of good examples of integration tests in plugins. Here’s the gradle-docker-plugin:

https://github.com/bmuschko/gradle-docker-plugin/blob/master/src/integTest/groovy/com/bmuschko/gradle/docker/ToolingApiIntegrationTest.groovy#L26-L41

I echo this recommendation. Using Ben’s “nebula-test” framework and the “IntegrationSpec” base class, you can dynamically construct a build file and the ecosystem the build runs in (it runs the build in a separate process, along with a temporary directory). It provides some tools that are convenient for setting up the build structure (creating dummy Java files, copying files into the temp dir, et cetera). If you surf the plugin portal, you’ll likely find quite a few plugins that use it (I wish it was easier to find these references).

FYI: Nebula-Test is a project maintained by Netflix. I don’t really “own” it.

This looks promising… thanks everyone.

I’ve been having another look at this, and it all seems fairly straight forward… but of course, it needs source code and other files for it to work correctly, it needs to build a build directory, etc. Where do you put that stuff? Just build a “project directory” in a subdirectory inside the Gradle plugin directory? Any gotchas about where it should go, etc.?

Thanks.

Soon, Gradle will ship with a test kit (see the nightly doc https://docs.gradle.org/nightly/userguide/test_kit.html) to ease the integ test writing

Stewart, it helps to browse through the plugins in the portal that use nebula-test. Unfortunately, there’s no easy way to quickly search for it, but quite a few of them use it.

You don’t need to manually create anything in your project directory. Operations provided by nebula-test do (almost) everything that you need.

Here is a very short and abbreviated example from the plugin that I wrote:

class YangPluginIntegSpec extends IntegrationSpec {
    def 'yangFilesRootDir exists'() {
        when:
        directory("src/main/yang")
        buildFile << applyPlugin(YangPlugin)
        buildFile << '''
            yang {
                yangFilesRootDir 'src/main/yang'
            }
        '''.stripIndent()

        ExecutionResult result = runTasksSuccessfully('build')

        then:
        !result.wasUpToDate("yangGenerate")
    }

You don’t need to know anything about my plugin to understand what this is doing. It constructs a very small build file (with “applyPlugin”), runs the build, and then verifies that a particular task is up to date.

When the test runs, nebula-test creates a temporary directory (in “build”) for your project, and then spawns a process to run the build. Note that nebula-test will not clean up this tempdir when the test completes. If you go a long time without “clean”, you’ll find LOTS of temporary directories in there.

@David_Karr I’ve had a look at some of the nebula-test examples… but there was another example from @sterling to the gradle-docker-plugin that looks like it’s only using the Tooling API, and not nebula-test: https://github.com/bmuschko/gradle-docker-plugin/blob/master/src/integTest/groovy/com/bmuschko/gradle/docker/ToolingApiIntegrationTest.groovy#L26-L41

Is there a lot that nebula-test provides that the Tooling API doesn’t?

Thanks @David_Karr and @sterling.

Stewart