My goal is to execute groovy script with binding, where functions are predefined and interceptor log out execution time and result of closure evaluation. My sample code is:
binding.login = { ->
binding.event.appname=='login'
} def gse = new GroovyScriptEngine("src/main/resources/rules")
gse.run('DSL.groovy', binding)
Inside my script I am making a call to login method. Everything works except I can't fugure out how to intercept it using MetaClass. My attempts like
Binding.metaClass.invokeMethod = { String name, args ->
println ("Call to $name intercepted... ")
did not work. Later I figured out that closure is a property of the binding, not a method.
Is there any way to perform interception in this scenario and how to do it? What would be a correct object for metaclass? As of note, my closure executed inside another nested closures.
Thanks
I don't know if it's the better solution, but i managed to do what you wanted by decorating the closures in the binding
binding = new Binding([
login : { -> println "binding.login called" },
echo : { String text -> println "test for $text" },
foo : { a, b, c -> println "$a $b $c" }
])
binding.variables.each { key, value ->
if (value instanceof Closure)
{
binding.variables[key] = { Object[] args ->
long t0 = System.currentTimeMillis()
value( *args )
print "[$key method"
print " args: $args "
println " time: ${System.currentTimeMillis() - t0}]"
}
}
}
def gse = new GroovyScriptEngine(".")
gse.run('Bind.groovy', binding)
And this is my Bind.groovy:
println " ** executing bind"
login()
echo("echo")
foo("test", 4, "echo")
println " ** bind script done"
You could also try/catch a MissingMethodException if you didn't defined the method as a closure in the binding.
I also recommend you Laforge's slideshare in creating DSLs:
http://www.slideshare.net/glaforge/going-to-mars-with-groovy-domainspecific-languages
In this slideshare, Laforge shows binding using a class that extends script; i think that's a good approach. More OO.
Update
Take a look at mrhaki's suggestion to delegate method calls to a base Script class (also in Guillaume' slideshare):
http://mrhaki.blogspot.com.br/2011/11/groovy-goodness-create-our-own-script.html
I just used it to implement a DSL over JFugue and it worked right away.
Related
In Jenkins pipelines, if I assign any key-value to env, I can access it like normal variables in string interpolation and environment variables in shell script. How does this work?
I see the probable implementation here but can't figure out how it works.
[Update]
In below code snippet, I can access the env properties without accessor -->
node {
stage('Preparation') {
env.foo = "bar"
echo "foo is $foo"
}
}
I haven't delved into the Jenkins code, but you could implement something like this by implementing the propertyMissing() method in a class, which would write to the script binding. The propertyMissing method gets called when the code is trying to access a property that is not declared in the class.
class MyEnv {
groovy.lang.Script script
MyEnv( groovy.lang.Script script ) {
this.script = script
}
def propertyMissing( String name ) {
script.getProperty( name )
}
def propertyMissing( String name, value ) {
script.setProperty( name, value )
}
}
def env = new MyEnv( this ) // pass the script context to the class
env.foo = 42 // actually assigns to the script binding
println "env.foo: $env.foo" // prints 42
println "foo: $foo" // prints 42
// It also works the other way around...
foo = 21 // assign to the script binding (Note: "def foo" would not work!)
println "env.foo: $env.foo" // prints 21
Extract from class CUT under test:
def compileOutputLines( TopDocs topDocs ) {
println "gubbins"
}
Test code:
def "my feature"(){
given:
CUT stubCut = Stub( CUT ){
compileOutputLines(_) >> { TopDocs mockTD ->
// NB no Exception is thrown
// try {
println "babbles"
callRealMethod()
println "bubbles"
// }catch( Exception e ) {
// println "exception $e"
// }
}
}
CUT spyCut = Spy( CUT ){
compileOutputLines(_) >> { TopDocs mockTD ->
println "babbles 2"
callRealMethod()
println "bubbles 2"
}
}
when:
stubCut.compileOutputLines( Mock( TopDocs ))
spyCut.compileOutputLines( Mock( TopDocs ))
then:
true
}
Output to stdout:
babbles
bubbles
babbles 2
gubbins
bubbles 2
I tried to find a link online to the full Spock Framework Javadoc... but I couldn't find it... the "non-framework" Javadoc is here, but you won't find the method callRealMethod in the index.
From the Javadoc API I have generated locally from the source, I can indeed find this method: it is a method of org.spockframework.mock.IMockInvocation. It says:
java.lang.Object callRealMethod()
Delegates this method invocation to the real object underlying this
mock object, including any method arguments. If this mock object has
no underlying real object, a CannotInvokeRealMethodException is
thrown.
Returns:
the return value of the method to which this invocation was delegated
My understanding (such as it is) is that a Stub should cause this Exception to be thrown. But it doesn't appear to be. Any comment from a passing expert?
Preface
This is an interesting question. In theory my answer would be:
callRealMethod() is only available for spies, not for mocks or stubs. It is also only mentioned in the chapter about spies, did you notice?
Think about it: A spy wraps a real object, so there you have a reference to a real method you can call. The same is not true for mocks and stubs which are just no-op subclasses. If you could call a real method for a stub, it would be a spy.
The puzzle
In reality I am seeing a different, even weirder behaviour from yours in my own test with Spock 1.1 (Groovy 2.4): No matter if I use a mock, stub or spy, callRealMethod() always calls the real method. This is really a surprise. So, yes, the behaviour is different from what I would have expected. Looking through the interface implementation's source code while debugging, I also cannot see any checks for the type of mock object (is it a spy or not?). The real method is just identified and called.
The solution
Looking at class DynamicProxyMockInterceptorAdapter I found the explanation for this behaviour: The exception mentioned in the IMockInvocation Javadoc is only thrown when trying to call the real method for an interface type mock, never for mocks or class type objects:
public Object invoke(Object target, Method method, Object[] arguments) throws Throwable {
IResponseGenerator realMethodInvoker = (ReflectionUtil.isDefault(method) || ReflectionUtil.isObjectMethod(method))
? new DefaultMethodInvoker(target, method, arguments)
: new FailingRealMethodInvoker("Cannot invoke real method '" + method.getName() + "' on interface based mock object");
return interceptor.intercept(target, method, arguments, realMethodInvoker);
}
So the sentence "if this mock object has no underlying real object, a (...)Exception is thrown" is in essence correct, but ambiguous because it does not explain what "underlying real object" means. Your assumption was just wrong, so was mine. Lesson learned for both of us.
Now when would you see the described behaviour?
package de.scrum_master.stackoverflow;
public interface MyInterface {
void doSomething();
}
package de.scrum_master.stackoverflow
import org.spockframework.mock.CannotInvokeRealMethodException
import spock.lang.Specification
class MyInterfaceTest extends Specification {
def "Try to call real method on interface mock"() {
given:
MyInterface myInterface = Mock() {
doSomething() >> { callRealMethod() }
}
when:
myInterface.doSomething()
then:
thrown(CannotInvokeRealMethodException)
}
def "Try to call real method on interface stub"() {
given:
MyInterface myInterface = Stub() {
doSomething() >> { callRealMethod() }
}
when:
myInterface.doSomething()
then:
thrown(CannotInvokeRealMethodException)
}
def "Try to call real method on interface spy"() {
given:
MyInterface myInterface = Spy() {
doSomething() >> { callRealMethod() }
}
when:
myInterface.doSomething()
then:
thrown(CannotInvokeRealMethodException)
}
}
Update: I have just created issue #830 requesting improvements in Spock's documentation.
Currently I'm trying to register findFiles step.
My set up is as follows:
src/
test/
groovy/
TestJavaLib.groovy
vars/
javaLib.groovy
javaApp.jenkinsfile
Inside TestJavaApp.groovy I have:
...
import com.lesfurets.jenkins.unit.RegressionTest
import com.lesfurets.jenkins.unit.BasePipelineTest
class TestJavaLibraryPipeline extends BasePipelineTest implements RegressionTest {
// Some overridden setUp() which loads shared libs
// and registers methods referenced in javaLib.groovy
void registerPipelineMethods() {
...
def fileList = [new File("testFile1"), new File("testFile2")]
helper.registerAllowedMethod('findFiles', { f -> return fileList })
...
}
}
and my javaLib.groovy contains this currently failing part:
...
def pomFiles = findFiles glob: "target/publish/**/${JOB_BASE_NAME}*.pom"
if (pomFiles.length < 1) { // Fails with java.lang.NullPointerException: Cannot get property 'length' on null object
error("no pom file found")
}
...
I have tried multiple closures returning various objects, but everytime I get NPE.
Question is - how to correctly register "findFiles" method?
N.B. That I'm very new to mocking and closures in groovy.
Looking at the source code and examples on GitHub, I see a few overloads of the method (here):
void registerAllowedMethod(String name, List<Class> args = [], Closure closure)
void registerAllowedMethod(MethodSignature methodSignature, Closure closure)
void registerAllowedMethod(MethodSignature methodSignature, Function callback)
void registerAllowedMethod(MethodSignature methodSignature, Consumer callback)
It doesn't look like you are registering the right signature with your call. I'm actually surprised you aren't getting a MissingMethodException with your current call pattern.
You need to add the rest of the method signature during registration. The findFiles method is taking a Map of parameters (glob: "target/publish/**/${JOB_BASE_NAME}*.pom" is a map literal in Groovy). One way to register that type would be like this:
helper.registerAllowedMethod('findFiles', [Map.class], { f -> return fileList })
I also faced the same issue. However, I was able to mock the findFiles() method using the following method signature:
helper.registerAllowedMethod(method('findFiles', Map.class), {map ->
return [['path':'testPath/test.zip']]
})
So I found a way on how to mock findFiles when I needed length property:
helper.registerAllowedMethod('findFiles', [Map.class], { [length: findFilesLength ?: 1] })
This also allows to change findFilesLength variable in tests to test different conditions in pipeline like the one in my OP.
Ran into this while doing a refactor. Calls to getProperties() were causing our CPU usage to spike. What we discovered is that if you have a getter without an associated attribute, when you make a call to getProperties() that getter is called over 1000 times. The fix/workaround is obvious and we know it has something to do with metaprogramming but why is this happening (what point in the groovy source)? See groovy script code below:
class tester {
int count = 0
public getVar() {
println count++ + " getVar() called!"
return var
}
}
def t = new tester()
t.getProperties()
println "done!"
You should see getVar() called over 1000 times. 1068 to be exact for us.
The question has probably already been answered in the comments but I dug a little deeper to also answer the "what point in the groovy source" part.
When you call getProperties() on the instance of tester Groovy will do its magic and finally call DefaultGroovyMethods#getProperties(Object) which (in Groovy 2.4.7) looks like this:
public static Map getProperties(Object self) {
List<PropertyValue> metaProps = getMetaPropertyValues(self); // 1
Map<String, Object> props = new LinkedHashMap<String, Object>(metaProps.size());
for (PropertyValue mp : metaProps) {
try {
props.put(mp.getName(), mp.getValue()); // 2
} catch (Exception e) {
LOG.throwing(self.getClass().getName(), "getProperty(" + mp.getName() + ")", e);
}
}
return props;
}
First, Groovy determines the meta properties of the given object (see 1). This will return three properties:
var: getter only (getVar()), no setter, no field
class: getter only (inherited from Object), no setter, no field
count: getter, setter (both generated by Groovy) and field
You can easily verify this by calling t.getMetaPropertyValues().
Next, Groovy tries to get the current value of each property and puts it in a map (see 2). When it reaches var, it remembers that var has a getter (namely getVar()) and calls it. getVar() however, returns var again. For Groovy, this is the exact same property as determined in the first step. Once again, it calls its getter getVar() and the endless loop begins.
At some point, depending on the JVM, this results in a StackOverflowError, which is exactly what this site is all about :-D
In Grails (at least up to the current version 2.2), taglibs are closures. For certain taglib closures, I'd like to use some "around" type advice / wrap the closure in an interceptor.
To put it another way, let's say there is this taglib straight from the Grails doc:
class SimpleTagLib {
def emoticon = { attrs, body ->
out << body() << (attrs.happy == 'true' ? " :-)" : " :-(")
}
}
Now, without modifying the taglib code, I'd like to time how long "emoticon" takes to execute.
Spring AOP (and all other AOP I can find) only seems to work on Java methods - and taglibs are always based on closures. An "around" pointcut would be perfect for this, but I can't figure out how to make it work.
I've written something similar which I put as a public closure in a category I then mixin to services:
// TimingCategory.groovy
/**
* Provides common AOP timing functionality to Services which mixin this category.
*/
class TimingCategory {
static Closure timer = { String label = "The call", Closure closure ->
Long start = System.currentTimeMillis()
def result = closure.call()
Long end = System.currentTimeMillis()
Long duration = end - start
log.warn "${label} took ${duration} ms"
return result
}
}
In other classes, you just reference the timer closure as such:
#Mixin(TimingCategory)
public class WhateverService {
public String doSomeWork() {
timer "Doing a lot of work", {
1000.times { doSomething() }
someMethodWithAStringReturnValue()
}
}
}
That will give you log output of "WARN: Doing a lot of work took nn ms" and return the value of the inner closure as the return value of the doSomeWork method.
For your taglib instance, just wrap the out << ... in the
timer "Writing an emoticon", {
// your code
}
code.
If you don't care about passing through the internal return value, you could instead return the duration as the result of the closure invocation.
Update:
I might have misread -- you're asking how to wrap the taglib execution without modifying the taglib code at all? What about creating a custom taglib which accepts the body and passes it to the other taglibs for execution?
I haven't tried this, but something like:
class TimedTagLib {
static namespace = "timer"
def timedTag = { attrs, body ->
timer "Executing the timed tag", {
out << body()
}
}
}
And invoking it like
<timer:timedTag><g:emoticon whatever="something">Text</g:emoticon></timer:timedTag>
Update 2:
Ok, so I tried it. Works fine. My final code (I added a second timer closure which returns the duration):
// TimedTagLib.groovy
#Mixin(TimingCategory)
class TimedTagLib {
static namespace = "timer"
def timedTag = { attrs, body ->
def duration = returnTimer "Printing the timed tag", {
out << body()
}
out << "Took ${duration} ms to print"
}
}
And the view:
// someView.gsp
<timer:timedTag>
<g:formatLocalDate date="${LocalDate.now()}" />
</timer:timedTag>
The resulting HTML is:
03/19/2013
Took 6 ms to print
And it also wrote to the log.