[jvm] [gradle] Cucumber Java Process not exiting properly

644 views
Skip to first unread message

Tristram Coffin

unread,
Oct 20, 2013, 6:56:45 PM10/20/13
to cu...@googlegroups.com
In an attempt to make gradle run multiple feature files concurrently, I've tweaked the typical cucumber task, and it's actually (kind of) working.  The project is here (on branch 'concurrent'): https://github.com/theaberrant/cucumber-jvm-groovy-rest-example/tree/concurrent

However, the java process doesn't exist properly - I see it in the running processes, but it has to be killed before the cucumber task completes.

If I just use:
def concurrentMethod(String fileName, String threadName) {
    def args = ['-f', 'pretty', '--glue', 'src/test/groovy', 'src/test/resources'].join(" ")
    def classpath = configurations.cucumberRuntime.getAsPath()
    def main = "cucumber.api.cli.Main"
    logger.quiet "FileName: $fileName, ThreadName: $threadName"
    logger.quiet "Running command: \"java -cp $classpath $main $args\""
    def process = "java -cp $classpath $main $args".execute()
    process.waitForOrKill(25000)
    logger.quiet "Process Out: ${process.text}"
    logger.quiet "Process Err: ${process.err}"
}

Using the following doesn't work, since the java process never exits:
process.waitFor()

My hack solution is to add a kill timeout, however that's just to see it work.

Any ideas why the java process doesn't exit?  This does work when attempting to run a groovyscript file, as seen in my other project: https://github.com/theaberrant/gradle-concurrent.  Any help would be greatly appreciated.  Further technical details below.

  Thanks,
     Tris

Cucumber-jvm: 1.1.5
$ gradle --version

------------------------------------------------------------
Gradle 1.4
------------------------------------------------------------

Gradle build time: Monday, January 28, 2013 3:42:46 AM UTC
Groovy: 1.8.6
Ant: Apache Ant(TM) version 1.8.4 compiled on May 22 2012
Ivy: 2.2.0
JVM: 1.7.0_13 (Oracle Corporation 23.7-b01)
OS: Windows 7 6.1 amd64

My entire build.gradle (for anyone who doesn't want to look at github):
import groovyx.gpars.GParsPool

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

//build stuff
sourceCompatibility = 1.6
targetCompatibility = 1.6

configurations {
    cucumberRuntime {
        extendsFrom testRuntime
    }
}

task cucumber() {
    dependsOn assemble
    doLast {
        javaexec {
            main = "cucumber.api.cli.Main"
            classpath = configurations.cucumberRuntime
            args = ['-f', 'pretty', '--glue', 'src/test/groovy', 'src/test/resources']
        }
    }
}

buildscript {
    repositories {
        mavenCentral()
    }

    dependencies {
        classpath "org.codehaus.gpars:gpars:1.1.0"
    }
}

dependencies {
    // Groovy library for groovy building!
    groovy 'org.codehaus.groovy:groovy-all:2.1.0'
    groovy 'org.codehaus.groovy.modules.http-builder:http-builder:0.6'
    groovy 'org.slf4j:slf4j-api:1.7.+'
    groovy 'org.slf4j:slf4j-log4j12:1.7.5'

    //In order to work around bug http://issues.gradle.org/browse/GRADLE-732
    cucumberRuntime files("${jar.archivePath}")

    testCompile 'junit:junit:4.11'
    testCompile 'info.cukes:cucumber-junit:1.1.5'
    testCompile 'info.cukes:cucumber-groovy:1.1.5'
}

repositories {
    mavenCentral()
}

def concurrentMethod(String fileName, String threadName) {
    def args = ['-f', 'pretty', '--glue', 'src/test/groovy', 'src/test/resources'].join(" ")
    def classpath = configurations.cucumberRuntime.getAsPath()
    def main = "cucumber.api.cli.Main"
    logger.quiet "FileName: $fileName, ThreadName: $threadName"
    logger.quiet "Running command: \"java -cp $classpath $main $args\""
    def process = "java -cp $classpath $main $args".execute()
    process.waitForOrKill(25000)
    logger.quiet "Process Out: ${process.text}"
    logger.quiet "Process Err: ${process.err}"
}

String getThreadName(names) {
    String currentName = Thread.currentThread().name
    if (!names.contains(currentName)) names << currentName

    "t${names.indexOf(currentName).toString().padLeft(2, '0')}"
}


task concurrentCucumber() {
    dependsOn assemble
    def cores = Runtime.runtime.availableProcessors()
    def threads = 10
    println "    > Using $threads threads on $cores cores..."
    def names = []
    GParsPool.withPool(threads) {
        def features = fileTree(dir: 'src/test/resources').include '**/*.feature'
        features.eachParallel { File file ->
            def name = getThreadName(names)
            logger.quiet "File: ${file.name}"
            concurrentMethod(file.name, name)
        }
    }
    logger.quiet("Run complete!")
}

task wrapper(type: Wrapper) {
    jarFile = 'wrapper/gradle-wrapper.jar'
    gradleVersion = "1.8"
}



Tristram Coffin

unread,
Oct 21, 2013, 8:02:39 PM10/21/13
to cu...@googlegroups.com


On Sunday, October 20, 2013 5:56:45 PM UTC-5, Tristram Coffin wrote:
In an attempt to make gradle run multiple feature files concurrently, I've tweaked the typical cucumber task, and it's actually (kind of) working.  The project is here (on branch 'concurrent'): https://github.com/theaberrant/cucumber-jvm-groovy-rest-example/tree/concurrent


I've pushed an update that works on my local machine.  I wasn't specifying the specific feature file, so I suspect something locked on trying to write?  Not entirely sure... 
 
However, the java process doesn't exist properly - I see it in the running processes, but it has to be killed before the cucumber task completes.

If I just use:
def concurrentMethod(String fileName, String threadName) {
    def args = ['-f', 'pretty', '--glue', 'src/test/groovy', 'src/test/resources'].join(" ")
    def classpath = configurations.cucumberRuntime.getAsPath()
    def main = "cucumber.api.cli.Main"
    logger.quiet "FileName: $fileName, ThreadName: $threadName"
    logger.quiet "Running command: \"java -cp $classpath $main $args\""
    def process = "java -cp $classpath $main $args".execute()
    process.waitForOrKill(25000)
    logger.quiet "Process Out: ${process.text}"
    logger.quiet "Process Err: ${process.err}"
}

Using the following doesn't work, since the java process never exits:
process.waitFor()

My hack solution is to add a kill timeout, however that's just to see it work.

Any ideas why the java process doesn't exit?  This does work when attempting to run a groovyscript file, as seen in my other project: https://github.com/theaberrant/gradle-concurrent.  Any help would be greatly appreciated.  Further technical details below.


I'll add some stuff like separated logging (I use jUnit report at work, to integrate with Bamboo), and tags / scenario support (command line parameters for gradle).

Let me know if anyone has suggestions for cleaning this up, or knows why passing a directory caused Cucumber to not exit properly.

  Thanks,
     Tris

Robert

unread,
Oct 21, 2013, 9:15:56 PM10/21/13
to cu...@googlegroups.com
Tim,

Is this working for you now? I'm starting to come up to speed with Groovy and would like to see if I could somehow mix this in with my existing java test code.
 

  Thanks,
     Tris

Tristram Coffin

unread,
Oct 21, 2013, 9:44:27 PM10/21/13
to cu...@googlegroups.com
Yes, if you grab the repo from: https://github.com/theaberrant/cucumber-jvm-groovy-rest-example

That has a working example.  I'll update the readme, but you can then run the following (on a linux system or cygwin):
dos2unix gradlew
./gradlew concurrentCucumber

Or if you have gradle installed, run:
gradle concurrentCucumber

The typical 'cucumber' gradle task is still there.  I'm curious if this will work on machines other than my own.

The only thing unique with my project versus the examples is that I'm running each feature file concurrently.  Additionally, this is testing the httpbin.org site, since that's an example that relates to testing a RESTful interface.  This is my test project for things I need to figure out :)

I'm still learning as I go, so use my examples with a grain of salt :)

  Tris

David Kowis

unread,
Nov 8, 2013, 6:34:19 PM11/8/13
to cu...@googlegroups.com
I think for this idea, I'd write a different Main class that executes
your Cucumbers in parallel, and handles all the wrapping and possibly
reading in of success and failure outside of the Gradle constraints.

Then just call that ParallelMain from the gradle task like normal.

--
David
Reply all
Reply to author
Forward
0 new messages