How is spock calling this function in this test? - groovy

I'm going through this example but something about it is very confusing to me: https://www.testcookbook.com/book/groovy/jenkins/intro-testing-job-dsl.html
In this test, how/what is executing getJobFiles()? I don't see it being called anywhere. Is there some magic with jobFiles? Is specifying jobFiles somehow calling getJobFiles?
import javaposse.jobdsl.dsl.DslScriptLoader
import javaposse.jobdsl.plugin.JenkinsJobManagement
import org.junit.ClassRule
import org.jvnet.hudson.test.JenkinsRule
import spock.lang.Shared
import spock.lang.Specification
import spock.lang.Unroll
class JobScriptsSpec extends Specification {
#Shared
#ClassRule
JenkinsRule jenkinsRule = new JenkinsRule()
#Unroll
def 'test script #file.name'(File file) {
given:
def jobManagement = new JenkinsJobManagement(System.out, [:], new File('.'))
when:
new DslScriptLoader(jobManagement).runScript(file.text)
then:
noExceptionThrown()
where:
file << jobFiles
}
static List<File> getJobFiles() {
List<File> files = []
new File('jobs').eachFileRecurse {
if (it.name.endsWith('.groovy')) {
files << it
}
}
files
}
}
Edit
It seems like jobFiles does call getJobFiles() but I don't understand how. Is this a groovy or spock feature? I've been trying to research this but can finding anything explaining this in detail.

This is standard Groovy functionality. You can abbreviate any getter call like
def file = new File("sarek-test-parent/sarek-test-common/src/main/java")
println file.name // getName()
println file.parent // getParent()
println file.absolutePath // getAbsolutePath()
println file.directory // isDirectory()
java
sarek-test-parent\sarek-test-common\src\main
C:\Users\alexa\Documents\java-src\Sarek\sarek-test-parent\sarek-test-common\src\main\java
true
The same works for setters:
new Person().name = "John" // setName("John")
new Person().zipCode = "12345" // setZipCode("12345")
Actually the second link provided by jaco0646 explains it, just his mixing up this simple fact with data providers clouds the explanation.

Edit
When Groovy determines that jobFiles does not refer to any existing variable, it considers the name as a Groovy property, which then allows it to take advantage of the shortcut notation for accessing properties.

Related

Does a GroovyMock of a non-static method have a specified behaviour?

I put this in my Spock test:
GroovyMock( File, global: true)
File.createNewFile() >> null
... which I realise is unorthodox/silly/curious: createNewFile is a non-static method.
The code involved is like this:
if( indexInfoFile.createNewFile() ) {
... it turns out from my experiements that mocking createNewFile like this always returns false, even if you try putting a block in the mock:
GroovyMock( File, global: true)
File.createNewFile() >> {
log.info( 'Hello mum!')
}
... the log message is not printed but createNewFile again returns false.
This is actually what I wanted (i.e. to mock a false return from createNewFile).
Is this intentional, documented behaviour?
PS Caveat: from my experience/experiments today, there is no doubt that this mock method does replace all occurrences of an invocation of this method, on any File instance. However, it appears also to have some alarming side-effects too: for example, a directory I created in my given block before the 2 GroovyMock lines is found NOT to exist afterwards, still in the given block, when I went
myDirPath.toFile().exists()
... I assume this is because toFile involves an invocation of createNewFile...
As documented, Groovy mocks only have additional "magic" when used with Groovy classes, but I assume that you are trying to mock java.io.File, which is a Java JRE class. Thus, the Groovy mock will behave like a normal Spock mock. So I don't know why you want to use the Groovy mock in the first place - maybe because you want to use the global: true feature in order to avoid refactoring for testability in your application class.
As you do not show us an MCVE, I have no way of knowing whether indexInfoFile can be injected into your class/method under test or if it is a dependency created inside the method. In the latter case you need to refactor, it is as simple as that. Dependencies should be injectable, period.
As for your code snippets, there are a few things wrong with them:
Method File.createNewFile() returns boolean, so it does not make any sense to stub it to return null.
When creating a mock, all methods will automatically return false, null or 0, depending on their return type. So there is no need to stub the result for createNewFile() in the first place if you want it to return false because it already does.
You cannot stub an instance method by trying to override it like it was a static method. It makes no sense. Please learn Spock syntax first.
Now, assuming your class under test looks like this (already prepared or refactored for dependency injection via method argument, constructor argument or setter)...
package de.scrum_master.stackoverflow.q59842227;
import java.io.File;
import java.io.IOException;
import java.util.Random;
public class FileCreator {
private static final Random RANDOM = new Random();
public boolean createIndexInfoFile(File indexInfoFile) throws IOException {
if (indexInfoFile.createNewFile()) {
System.out.println("File \"" + indexInfoFile + "\" created");
return true;
}
System.out.println("File \"" + indexInfoFile + "\" NOT created");
return false;
}
public static void main(String[] args) throws IOException {
new FileCreator().createIndexInfoFile(
new File("_abc_" + RANDOM.nextInt(10000) + ".txt")
);
}
}
... then you can test it like this:
package de.scrum_master.stackoverflow.q59842227
import spock.lang.Specification
class FileCreatorTest extends Specification {
def "index info file created"() {
given:
File file = Mock() {
createNewFile() >> true
}
expect:
new FileCreator().createIndexInfoFile(file)
}
def "no index info file created"() {
given:
File file = Mock()
expect:
!new FileCreator().createIndexInfoFile(file)
}
}
See? There is no need for global or Groovy mocks, normal mocks will do just fine. But you need to make your code testable instead of using fancy tricks.

Groovy Compile time AST transformation: Assignment to a field

I'm currently trying to implement some Groovy compile time AST transformations, but I ran into trouble:
How do I specify an AST transformation for an assignment statement to a field? i.e. the AST transformation should transform the following code:
class MyClass {
#MyTransformation
String myField
public void init() {
}
}
into something like
class MyClass {
String myField
public void init() {
this.myField = "initialized!"
}
}
I tried it with this AST builder invocation:
def ast = new AstBuilder().buildFromSpec {
expression{
declaration {
variable "myField"
token "="
constant "initialized!"
}
}
}
But after inserting the resulting statement in the "init" method of the declaring class, it instead inserted a variable assignment, as in
java.lang.Object myField = "initialized!"
I looked through the examples incorporated in the Ast Builder TestCase, but they only cover field declaration in the class body, not assignments to fields. My own tries using fieldNode all resulted in compiler errors. I set the compile phase to INSTRUCTION_SELECTION; I think this should be fine.
How do I achieve this? A solution based on the AstBuilder#buildFromSpec method is preferred, but any help would be highly appreciated.
I usually recommand not to use the AST builder. It's good for prototyping, but you don't really control what it generates. In particular, here, it's not capable of handling the fact that the variable expression you create should reference the field node. AST Builder is very nice to learn about the AST, but shouldn't be used in production code IMHO.
Here is a self contained example that demonstrates how you can acheive what you want. The code inside #ASTTest would correspond to your transform code:
import groovy.transform.ASTTest
import org.codehaus.groovy.ast.expr.BinaryExpression
import org.codehaus.groovy.ast.expr.VariableExpression
import org.codehaus.groovy.ast.expr.ConstantExpression
import org.codehaus.groovy.ast.stmt.ExpressionStatement
import org.codehaus.groovy.syntax.Token
import org.codehaus.groovy.syntax.Types
class MyClass {
String myField
#ASTTest(phase=SEMANTIC_ANALYSIS,value={
def classNode = node.declaringClass
def field = classNode.getDeclaredField('myField')
def assignment = new BinaryExpression(
new VariableExpression(field),
Token.newSymbol(Types.EQUAL, 0, 0),
new ConstantExpression('initialized!')
)
node.code.addStatement(new ExpressionStatement(assignment))
})
public void init() {
}
}
def c = new MyClass()
c.init()
println c.myField
Hope this helps!

Groovy metaClass closures vs dynamic mixins

So I want to add methods to JDK classes like InputStream, File, etc. I'm trying to figure out what is the best way to do that, but it seems there are several options for doing it. One way is do this by adding methods into the metaClass property on the Class like so:
InputStream.metaClass.copy = { OutputStream out ->
long total = 0
byte[] buffer = new byte[8096]
int len
while ((len = read(buffer)) >= 0) {
out.write(buffer, 0, len)
total += len
}
out.flush()
return delegate
}
Another way is using dynamic mixins like this:
class EnhancedInputStream {
static {
InputStream.metaClass.mixin( EnhancedInputStream )
}
public InputStream copy( OutputStream out ) {
long total = 0
byte[] buffer = new byte[8096]
int len
while ((len = mixinOwner.read(buffer)) >= 0) {
out.write(buffer, 0, len)
total += len
}
out.flush()
return mixinOwner
}
}
So the first question is do dynamic Mixins replace the use of using metaClass + Closure to create mixins? The examples of dynamic mixins don't really discuss scoping rules in any detail that I can find. Which leads me to the next point.
You can see in the first code sample using metaClass I used delegate to get access to the this pointer of the class I was adding methods to. What is the equivalent way to do that using dynamic Mixins? All examples I've found are stateless (pointless really). I found one example mentioning a special member mixinOwner that could be used in place of delegate. Is that true?
Second you'll see I used a static block in EnhancedInputStream to add the mixin dynamically to InputStream. When using metaClass what is the best way to add those? Another static block with import statement?
I suppose I really want just a compile time Mixin where I can define the #Mixin on the source of the mixin instead of destination because I didn't write the destination. Like
#MixinInto(File)
public class EnhancedFileMixin {
public void zip( File output ) {
// .....
}
}
But that doesn't appear to exist in Groovy land. So what's the best approach to reach this using metaClass or dynamic mixins?
I guess the nearest to #MixinInto would be the magic package convention. I couldn't mix it into a interface, but i managed to mix it into a FileInputStream, if that suits your case. I guess you can add state using the MetaClass which comes in the constructor.
To write a class to be mixed into InputStream. It needs to be:
In the package groovy.runtime.metaclass.java.io
Named FileInputStreamMetaClass (exactly)
Compiled and put into the classpath
Extend DelegatingMetaClass
It can only intercept the GroovyObject methods, so it is not so straightforward. If you are in for a pure dynamic groovy, it is great:
package groovy.runtime.metaclass.java.io
class FileInputStreamMetaClass extends DelegatingMetaClass {
FileInputStreamMetaClass(MetaClass meta) {
super(meta)
println "built FileInputStreamMetaClass"
}
Object invokeMethod(Object delegate, String method, Object[] args) {
switch (method) {
case "copy":
return "i'm copying stuff"
default:
return super.invokeMethod(delegate, method, args)
}
}
}
Compiling:
$ groovyc FileInputStreamMetaClass.groovy
$ groovy -cp . InputTest.groovy
A test:
InputStream input = new FileInputStream("/tmp/test.tmp")
assert input.copy() == "i'm copying stuff"
A bit cumbersome.
I'd go for Extensions any time of the day. Three files:
// file META-INF/services/org.codehaus.groovy.runtime.ExtensionModule
moduleName=InputExtensionModule
moduleVersion=0.1
extensionClasses=InputStreamExtension
The extension:
class InputStreamExtension {
static String copy(InputStream input) {
"copying stuff, doc"
}
}
The test:
def input = new FileInputStream("/tmp/test.tmp")
assert input.copy() == "copying stuff, doc"
Compile and run:
$ groovyc InputStreamExtension.groovy
$ groovy ISExtensionTest.groovy
And i think the extension is the perfect place to use the static { mixin } block. With some changes:
class InputStreamExtension {
static {
InputStream.mixin InputStreamMixin
}
static String copy(InputStream input) { "copying stuff, doc" }
}
#Category(InputStream)
class InputStreamMixin {
Object pop() {
"input mixin pop"
}
}
A new test:
def input = new FileInputStream("/tmp/test.tmp")
assert input.copy() == "copying stuff, doc"
assert input.pop() == "input mixin pop"
Well I finally figured it out on my own. Essentially the this reference refers to the instance of the Mixin which doesn't do us much good. However, you can use the "as" keyword to convert that to the instance of the target class. For example:
class MyMixin {
static {
File mixin MyMixin
}
File foo() {
return this as File
}
}
File f = new File()
println( f.foo().equals( f ) )
As for mixinOwner and owner references that the jira bug refers to. They don't exist. This is the only way to get a reference to the instance that the mixin was added to.
I wrote up a longer blogpost about it because I thought this was important information for future Groovy programmers since there is zero official docs discussing this.
http://wrongnotes.blogspot.com/2013/06/groovy-mixins-and-this-pointer.html
I am glad you asked this question. To answer a very important question:
I suppose I really want just a compile time Mixin where I can define the #Mixin on the source of the mixin instead of destination because I didn't write the destination.
You cannot achieve this by #Mixin but we do have something in Groovy which will help you out. It is called #Category. Let me go through your example again to show you how you can actually effectively use this in category. Have a look at the below script:
#Category(InputStream)
class InputStreamCategory{
def copy(OutputStream out){
long total = 0
byte[] buffer = new byte[8096]
int len
while ((len = this.read(buffer)) >= 0) {
out.write(buffer, 0, len)
total += len
}
out.flush()
return this
}
}
class MyUtil{
def str = 'This is a dummy String!!!!'
InputStream inputS = new ByteArrayInputStream(str.bytes)
OutputStream outputS = new ByteArrayOutputStream()
def copy(){
use(InputStreamCategory){
inputS.copy(outputS)
println "Printing Output Stream: " + outputS
}
}
}
def util = new MyUtil()
util.copy()
//Prints:
Printing Output Stream: This is a dummy String!!!!
Explanation:-
#Category(InputStream) sets the this in InputStreamCategory and in your util class you just use the newly added method copy to InputStream. The benefit for using category is that you get hold of the caller object in this case inputS. The first parameter passed into a category always refers to the caller. You can have different categories for different implementations like FileCategory etc and then create an Utility Class to use those Categories. Therefore, you would end up with utilities like zip, copy, etc.
You could get detail information about the same from the api. I also highly recommend going through Category and Mixin Transformations.
To answer the first question:-
do dynamic Mixins replace the use of using metaClass + Closure to create mixins?
No they do not. metaClass implementation does not create a Mixin. It just adds another method in the metaData registry about the class while runtime. You get an handle to the delegate. On the other hand #Mixin gives you the ability to inherit pre-defined properties.

Black listing synchronized keyword for groovy scripts

There is an application where users can provide custom groovy scripts. They can write their own functions in those scripts. I want to restrict people from using the 'synchronized' keyword as well as some other keywords in these scripts. For example it should not be possible to write a function like below.
public synchronized void test() {
}
I am creating a CompilerConfiguration and using the SecureASTCustomizer. However adding org.codehaus.groovy.syntax.Types.KEYWORD_SYNCHRONIZED to the list of black listed tokens doesn't seem to do the job. (if I add org.codehaus.groovy.syntax.Types.PLUS it's preventing the usage of '+' within scripts.. but doesn't seem to do the job for synchronized)
Any ideas on how to achieve this ...
You can do something like this:
import org.codehaus.groovy.control.CompilerConfiguration
import org.codehaus.groovy.syntax.SyntaxException
import org.codehaus.groovy.ast.ClassNode
import org.codehaus.groovy.control.SourceUnit
import org.codehaus.groovy.classgen.GeneratorContext
class SynchronizedRemover extends org.codehaus.groovy.control.customizers.CompilationCustomizer {
SynchronizedRemover() {
super(org.codehaus.groovy.control.CompilePhase.CONVERSION)
}
void call(final SourceUnit source, final GeneratorContext context, final ClassNode classNode) {
classNode.methods.each { mn ->
if (mn.modifiers & 0x0020) { // 0x0020 is for synchronized
source.addError(new SyntaxException("Synchronized is not allowed", mn.lineNumber, mn.columnNumber))
}
}
}
}
def config = new CompilerConfiguration()
config.addCompilationCustomizers(new SynchronizedRemover())
def shell = new GroovyShell(config)
shell.evaluate '''
class Foo { public synchronized void foo() { println 'bar' } }
'''
The idea is to create a compilation customizer that checks generated classes and for each method, add an error if the synchronized modifier is present. For synchronized block inside methods, you can probably use the SecureASTCustomizer with a custom statement checker.

Extending spock specification gives error in code

Geb Groovy and spock gurus, could you please help me understand why the following code doesn't work for me ( the variable 'closr' gives a null value)
import geb.Browser
import spock.lang.Specification
class somclass extends Specification{
def clos = {
go()
}
def Browser driveit(Browser browser, Closure script) {
script.setDelegate(browser)
script()
browser
}
}
objnew = new somclass()
def closr = objnew.clos
objnew.driveit(new Browser(),closr)
While
import geb.Browser
class somclass {
def clos = {
go()
}
def Browser driveit(Browser browser, Closure script) {
script.setDelegate(browser)
script()
browser
}
}
objnew = new somclass()
def closr = objnew.clos
objnew.driveit(new Browser(),closr)
this one does work for me( variable 'closr' has the closure returned ).? The only difference in the second one was I removed extending spock specification
I don't know what you are trying to achieve with this code, but Spock can't be used in this way. Spock specifications have to be executed via JUnit, and they have to conform to certain rules (e.g. test methods have blocks like setup: or expect:).

Resources