Search code examples
groovy

Why does a Groovy script hang indefinitely after throwing an exception using the assert statement?


I have a verify.groovy script that executes multiple Groovy scripts using the evaluate() function. One of the scripts, first-check.groovy, intentionally throws an exception using the assert statement. However, instead of exiting gracefully with the error message, the verify.groovy script seems to hang indefinitely and never completes.

Here's the code for verify.groovy:

[
  'first-check.groovy',
  'second-check.groovy'
].each {
  evaluate(basedir.toPath().resolve(it).toFile())
  println String.format('Verified with %s - OK', it)
}

And the code for first-check.groovy:

assert 1 == 2
true

Exception:

Exception in thread "main" Assertion failed: 

assert 1 == 2
         |
         false

        at org.codehaus.groovy.runtime.InvokerHelper.assertFailed(InvokerHelper.java:432)

<--- Hang here until I interrupt the process by Ctrl+C or command+C

Why does the script hang indefinitely instead of completing with the error message, and how can I fix it?


Solution

  • The exception handling in each and alike methods is tricky, so you have to catch and re-throw the exception manually:

    String first = '''\
    assert 1 == 2 
    true'''
    String second = '''\
    assert true
    true'''
    
    [ first, second ].eachWithIndex{ it, ix ->
      try{
        def res = evaluate it
        println "Verified $ix with $res"
      }catch( Throwable t ){
        println "Check $ix failed miserably: $t"
        throw t // << re-throw!
      }
    }
    
    println 'end reached'
    

    In this case you will see this in the console:

    Check 0 failed miserably: Assertion failed: 
    
    assert 1 == 2
             |
             false
    Assertion failed: 
    
    assert 1 == 2
             |
             false
    
        at Script1.run(Script1.groovy:1)
        at Script1$_run_closure1.doCall(Script1.groovy:10)
        at Script1.run(Script1.groovy:8)
    

    If you comment the re-throw line out, the output will be:

    Check 0 failed miserably: Assertion failed: 
    
    assert 1 == 2
             |
             false
    
    Verified 1 with true
    end reached
    

    With that said, the better option to iterate through the scripts would be to use a plain for loop:

    int ix = 0
    for( it in [ first, second ] ){
      def res = evaluate it
      println "Verified $ix with $res"
      ix++
    }
    

    In this case the exception with be propagated without blocking.