groovy

@Grapes does not work (at least not in Groovy Console)

Details

  • Type: Bug Bug
  • Status: Closed Closed
  • Priority: Major Major
  • Resolution: Fixed
  • Affects Version/s: 1.6.5
  • Fix Version/s: 1.6.6, 1.7-rc-1
  • Component/s: Grape
  • Labels:
    None
  • Environment:
    Mac OS 10.6.1
    Java 1.6.0_15
  • Testcase included:
    yes
  • Number of attachments :
    1

Description

Try this in Groovy Console:

@Grapes(
  [@Grab(group='org.spockframework', module='spock-core', version='0.2'),
  @Grab(group="junit", module="junit", version="4.7")])
class HelloSpock extends Specification {
 def "can you figure out what I'm up to?"() {
   expect:
   name.size() == length

   where:
   name << ["Kirk", "Spock", "Scotty"]
   length << [4, 5, 6]
 }
}

Output:

1 compilation error:

unable to resolve class Specification 
 at line: 1, column: 1

This means that Spock is not on the compile class path. When I @Grab JUnit and Spock individually by introducing a fake class, script works as expected. (Well, not quite. I need to add JUnitCore.run(HelloSpock) because otherwise, the test class is no longer found.)

By the way, if Ivy was fully compatible with Maven, the JUnit @Grab wouldn't be necessary, because JUnit is a mandatory compile-time dependency of Spock. Admittedly, Ivy's transitive dependency resolution is more correct than Maven's, but at the cost of breaking compatibility. Is there a way to tweak this?

Issue Links

Activity

Hide
Roshan Dawrani added a comment -

I don't think that the compilation error you are getting here is due to any Grape bug. Your script should be like:

import spock.lang.Specification

@Grapes(
  [@Grab(group='org.spockframework', module='spock-core', version='0.2'),
  @Grab(group="junit", module="junit", version="4.7")])
class HelloSpock extends Specification {
.....
}

Since spock.lang.* package is not a default import into groovy, you need to tell in the script which package class Specification could be found in.

Also, looking at the grape executing your script, I noticed that it was retrieving junit-4.6 correctly as the dependency, so you should not need @Grab JUnit.

Can you first try with the spock.lang.Specification and then without the Junit @Grab?

Show
Roshan Dawrani added a comment - I don't think that the compilation error you are getting here is due to any Grape bug. Your script should be like:
import spock.lang.Specification

@Grapes(
  [@Grab(group='org.spockframework', module='spock-core', version='0.2'),
  @Grab(group="junit", module="junit", version="4.7")])
class HelloSpock extends Specification {
.....
}
Since spock.lang.* package is not a default import into groovy, you need to tell in the script which package class Specification could be found in. Also, looking at the grape executing your script, I noticed that it was retrieving junit-4.6 correctly as the dependency, so you should not need @Grab JUnit. Can you first try with the spock.lang.Specification and then without the Junit @Grab?
Hide
Peter Niederwieser added a comment -

I think I just lost the import at some point when investigating this issue. After fixing the import, I'm back at:

Exception thrown: org.junit.runner.JUnitCore

java.lang.ClassNotFoundException: org.junit.runner.JUnitCore

That's what you see after my recent commit in trunk. Otherwise you'll just get "Error running JUnit 4 test." Adding the JUnit @Grab with @Grapes doesn't make a difference, but adding it on a fake class solves the ClassNotFoundException problem:

import spock.lang.*

@Grab(group="junit", module="junit", version="4.7")
class Fake {}

@Grab(group='org.spockframework', module='spock-core', version='0.2')
class HelloSpock extends Specification {
def "can you figure out what I'm up to?"() {
  expect:
  name.size() == length

  where:
  name << ["Kirk", "Spock", "Scotty"]
  length << [4, 5, 6]
}
}

// we have to run the spec manually because GroovyShell doesn't know what to do if two classes are present
new org.junit.runner.JUnitCore().run(HelloSpock).failureCount
Show
Peter Niederwieser added a comment - I think I just lost the import at some point when investigating this issue. After fixing the import, I'm back at:
Exception thrown: org.junit.runner.JUnitCore

java.lang.ClassNotFoundException: org.junit.runner.JUnitCore
That's what you see after my recent commit in trunk. Otherwise you'll just get "Error running JUnit 4 test." Adding the JUnit @Grab with @Grapes doesn't make a difference, but adding it on a fake class solves the ClassNotFoundException problem:
import spock.lang.*

@Grab(group="junit", module="junit", version="4.7")
class Fake {}

@Grab(group='org.spockframework', module='spock-core', version='0.2')
class HelloSpock extends Specification {
def "can you figure out what I'm up to?"() {
  expect:
  name.size() == length

  where:
  name << ["Kirk", "Spock", "Scotty"]
  length << [4, 5, 6]
}
}

// we have to run the spec manually because GroovyShell doesn't know what to do if two classes are present
new org.junit.runner.JUnitCore().run(HelloSpock).failureCount
Hide
Roshan Dawrani added a comment -

I tried a change with which the following script is working fine (but only from 2nd attempt onwards - details follow).

import spock.lang.*

@Grab(group='org.spockframework', module='spock-core', version='0.2')
class HelloSpock extends Specification {
 def "can you figure out what I'm up to?"() {
   expect:
   name.size() == length

   where:
   ignored = println ("where: \n p0=$p0\n p1=$p1")
   name << ["Kirk", "Spock", "Scotty"]
   length << [4, 5, 6]
 }
}

Now, this code does not get executed correctly when you try for the first time. It fails with the error:

Test Failure: initializationError(HelloSpock)
org.spockframework.runtime.InvalidSpeckError: Class 'HelloSpock' is not a Speck, or has not been compiled properly

This is happening because before grab brings in spock-jar on the classpath, the scanning for AST transformations has already happened, so the class does not get a chance to undergo spock related AST transformations and when run, it is not recognized as a speck.

2nd time onwards that issue is not there as the classpath has spock-jar now and the script correctly undergoes spock's AST transformation also and it is then run correctly as JUnit4 test.

Since the AST transformation issue mention above is not related to the current JIRA, I want to commit the patch that makes the above script work(2nd time onwards) and mark this issue as fixed. Let me know if someone sees any issue with that.

I will be interested in knowing views about the AST transformation issue and how it might be taken forward. Should scanning for AST transformation happen once more after GrabAnnotationTransformation has fetched stuff and updated the classpath?

Show
Roshan Dawrani added a comment - I tried a change with which the following script is working fine (but only from 2nd attempt onwards - details follow).
import spock.lang.*

@Grab(group='org.spockframework', module='spock-core', version='0.2')
class HelloSpock extends Specification {
 def "can you figure out what I'm up to?"() {
   expect:
   name.size() == length

   where:
   ignored = println ("where: \n p0=$p0\n p1=$p1")
   name << ["Kirk", "Spock", "Scotty"]
   length << [4, 5, 6]
 }
}
Now, this code does not get executed correctly when you try for the first time. It fails with the error:
Test Failure: initializationError(HelloSpock)
org.spockframework.runtime.InvalidSpeckError: Class 'HelloSpock' is not a Speck, or has not been compiled properly
This is happening because before grab brings in spock-jar on the classpath, the scanning for AST transformations has already happened, so the class does not get a chance to undergo spock related AST transformations and when run, it is not recognized as a speck. 2nd time onwards that issue is not there as the classpath has spock-jar now and the script correctly undergoes spock's AST transformation also and it is then run correctly as JUnit4 test. Since the AST transformation issue mention above is not related to the current JIRA, I want to commit the patch that makes the above script work(2nd time onwards) and mark this issue as fixed. Let me know if someone sees any issue with that. I will be interested in knowing views about the AST transformation issue and how it might be taken forward. Should scanning for AST transformation happen once more after GrabAnnotationTransformation has fetched stuff and updated the classpath?
Hide
Roshan Dawrani added a comment -

Still waiting for comments - for the confirmation that the issue that now remains has nothing to do with this JIRA (see previous comment for details) - before I go ahead and apply the patch.

Show
Roshan Dawrani added a comment - Still waiting for comments - for the confirmation that the issue that now remains has nothing to do with this JIRA (see previous comment for details) - before I go ahead and apply the patch.
Hide
blackdrag blackdrag added a comment -

Roshan, I am not clear what your patch will do. As on the issue directly... If the grab annotation would be visited first in ResolveClass, including fetching the dependencies, then it would work I guess. But to me it becomes more and more clear that we should go with the following... make an annoation resolving try in conversion. And make the second, complete scan including failures normally in ResolveVisitor.

Show
blackdrag blackdrag added a comment - Roshan, I am not clear what your patch will do. As on the issue directly... If the grab annotation would be visited first in ResolveClass, including fetching the dependencies, then it would work I guess. But to me it becomes more and more clear that we should go with the following... make an annoation resolving try in conversion. And make the second, complete scan including failures normally in ResolveVisitor.
Hide
Roshan Dawrani added a comment -

Attaching the patch here.

GroovyShell checked whether a class was a JUnit4 test or a TestNG test with the correct class loader but then executed the tests using a different class loader (Class.forName()). That is why it was not seeing the JUnit4 classes that the GroovyShell loader was seeing after @grab added spock/junit4 URLs to it.

The changes regarding the issue that will remain - possibly doing 2 scans to find transformations or some other approach - can be taken as an improvement/JIRA outside this one, right?

Show
Roshan Dawrani added a comment - Attaching the patch here. GroovyShell checked whether a class was a JUnit4 test or a TestNG test with the correct class loader but then executed the tests using a different class loader (Class.forName()). That is why it was not seeing the JUnit4 classes that the GroovyShell loader was seeing after @grab added spock/junit4 URLs to it. The changes regarding the issue that will remain - possibly doing 2 scans to find transformations or some other approach - can be taken as an improvement/JIRA outside this one, right?
Hide
Roshan Dawrani added a comment -

Although spock here is junit4 based, even execution of TestNG tests by GroovyShell had the same issue, so I corrected that as well here itself.

Show
Roshan Dawrani added a comment - Although spock here is junit4 based, even execution of TestNG tests by GroovyShell had the same issue, so I corrected that as well here itself.
Hide
blackdrag blackdrag added a comment -

the patch looks good to me. As for the transform recognition... yes, that should be an issue of its own

Show
blackdrag blackdrag added a comment - the patch looks good to me. As for the transform recognition... yes, that should be an issue of its own
Hide
Roshan Dawrani added a comment -

Jochen, I tried adding a unit test case for the scenario reported here, but had to revert it because the @Grab download of groovy jar failed on the bamboo box for some reason. I removed the test case because it was a shaky one anyway and one that I was able to put in only for 1.6.x version and not for trunk for the following reason:

  • Apart from JUnit 4.7, spock-2.0 also was bringing in groovy-all-1.6.3 in the classpath. With trunk version, even locally I was getting java.lang.NoSuchMethodError: org.codehaus.groovy.ast.ModuleNode.getImport(Ljava/lang/String;)Lorg/codehaus/groovy/ast/ClassNode;, which, I guess, was because GCL was now seeing 2 different groovy versions on the classpath.

I have locally tested it and I am fairly comfortable with it having tested it locally on 1.6.x and then you have seen it too.

Let me know if you see any issue in me marking it "fixed".

We can also wait for Peter to verify it from groovyconsole as reported in JIRA, if you want.

Show
Roshan Dawrani added a comment - Jochen, I tried adding a unit test case for the scenario reported here, but had to revert it because the @Grab download of groovy jar failed on the bamboo box for some reason. I removed the test case because it was a shaky one anyway and one that I was able to put in only for 1.6.x version and not for trunk for the following reason:
  • Apart from JUnit 4.7, spock-2.0 also was bringing in groovy-all-1.6.3 in the classpath. With trunk version, even locally I was getting java.lang.NoSuchMethodError: org.codehaus.groovy.ast.ModuleNode.getImport(Ljava/lang/String;)Lorg/codehaus/groovy/ast/ClassNode;, which, I guess, was because GCL was now seeing 2 different groovy versions on the classpath.
I have locally tested it and I am fairly comfortable with it having tested it locally on 1.6.x and then you have seen it too. Let me know if you see any issue in me marking it "fixed". We can also wait for Peter to verify it from groovyconsole as reported in JIRA, if you want.
Hide
Peter Niederwieser added a comment -

I can confirm that HelloSpock now works from the second run onwards. The reason why you are getting NoSuchMethodError is that getImport() has changed incompatibly between Groovy 1.6 and 1.7. Spock 0.2 only works with Groovy 1.6.

Show
Peter Niederwieser added a comment - I can confirm that HelloSpock now works from the second run onwards. The reason why you are getting NoSuchMethodError is that getImport() has changed incompatibly between Groovy 1.6 and 1.7. Spock 0.2 only works with Groovy 1.6.
Hide
Roshan Dawrani added a comment -

Thanks for confirming that, Peter.

That incompatibility of getImport() is what I also meant to point to point out when I mentioned that after @Grab(bing) spock, groovy is seeing both 1.6 and 1.7 on its classpath and is running into that error.

So, the plan is that we mark this JIRA as fixed as it has fixed the issue that was reported and then open another, linked JIRA to try to bring in some changes in AST transformation scanning so that this spock script can run from first attempt itself. Are you ok with that?

Show
Roshan Dawrani added a comment - Thanks for confirming that, Peter. That incompatibility of getImport() is what I also meant to point to point out when I mentioned that after @Grab(bing) spock, groovy is seeing both 1.6 and 1.7 on its classpath and is running into that error. So, the plan is that we mark this JIRA as fixed as it has fixed the issue that was reported and then open another, linked JIRA to try to bring in some changes in AST transformation scanning so that this spock script can run from first attempt itself. Are you ok with that?
Hide
Peter Niederwieser added a comment -

Sure! Thanks for your work, Roshan.

Show
Peter Niederwieser added a comment - Sure! Thanks for your work, Roshan.
Hide
Roshan Dawrani added a comment -

You're welcome, Peter.

Show
Roshan Dawrani added a comment - You're welcome, Peter.
Hide
Roshan Dawrani added a comment -

Fixed.

Show
Roshan Dawrani added a comment - Fixed.
Hide
Roshan Dawrani added a comment -

Hi Peter, I have fixed it in 1.6.6 now. Can you please try with the latest 1.6.6 snapshot and see if this spock script execution now works from groovyconsole - from the first run itself?

Thanks.

Show
Roshan Dawrani added a comment - Hi Peter, I have fixed it in 1.6.6 now. Can you please try with the latest 1.6.6 snapshot and see if this spock script execution now works from groovyconsole - from the first run itself? Thanks.
Hide
Peter Niederwieser added a comment -

Works fine now.

Show
Peter Niederwieser added a comment - Works fine now.

People

Vote (0)
Watch (1)

Dates

  • Created:
    Updated:
    Resolved: