Is the #Repeatable annotation not supported by Groovy? - groovy

I'm coding in Groovy and am having trouble with the Java 8 #Repeatable meta-annotation. I think I'm doing everything right, but it appears that Groovy is not recognizing #Repeatable. Here's my sample code; I'm expecting the information from both annotations to get stored in MyAnnotationArray:
import java.lang.annotation.*
class MyClass
{
#MyAnnotation(value = "val1")
#MyAnnotation(value = "val2")
void annotatedMethod()
{
println("annotated method called")
}
public static void main(String... args)
{
MyClass ob = new MyClass()
ob.annotatedMethod()
java.lang.reflect.Method m = ob.getClass().getMethod("annotatedMethod")
List annos = m.getAnnotations()
println("annos = $annos")
}
}
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
#Repeatable(MyAnnotationArray)
public #interface MyAnnotation
{
String value() default "val0";
}
public #interface MyAnnotationArray
{
MyAnnotation[] MyAnnotationArray()
}
What happens is that I get this error:
Caught: java.lang.annotation.AnnotationFormatError: Duplicate annotation for class: interface MyAnnotation: #MyAnnotation(value=val2)
java.lang.annotation.AnnotationFormatError: Duplicate annotation for class: interface MyAnnotation: #MyAnnotation(value=val2)
Which is exactly what I get if I leave out the #Repeatable meta-annotation.
The code works fine if I leave out one of the duplicate MyAnnotations; then there is no error, and I then can read the annotation value as expected.
Is it possible that Groovy doesn't support the #Repeatable meta-annotation? I couldn't find any documentation that states this outright, though this page hints that maybe this is the case (scroll down to item 88).

seems to be not supported
i used java 1.8 and groovy 2.4.11
after compiling and de-compilig the same code i got this:
java:
#MyAnnotationArray({#MyAnnotation("val1"), #MyAnnotation("val2")})
public void annotatedMethod()
{
System.out.println("annotated method called");
}
groovy:
#MyAnnotation("val1")
#MyAnnotation("val2")
public void annotatedMethod()
{
System.out.println("annotated method called");null;
}
so, as workaround in groovy use
//note the square brackets
#MyAnnotationArray( [#MyAnnotation("val1"), #MyAnnotation("val2")] )
public void annotatedMethod()
{
System.out.println("annotated method called");
}
full script (because there were some errors in annotation declaration)
import java.lang.annotation.*
class MyClass
{
//#MyAnnotation(value = "val1")
//#MyAnnotation(value = "val2")
#MyAnnotationArray( [#MyAnnotation("val1"), #MyAnnotation("val2")] )
public void annotatedMethod()
{
System.out.println("annotated method called");
}
public static void main(String... args)
{
MyClass ob = new MyClass()
ob.annotatedMethod()
java.lang.reflect.Method m = ob.getClass().getMethod("annotatedMethod")
List annos = m.getAnnotations()
println("annos = $annos")
}
}
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
#Repeatable(MyAnnotationArray)
public #interface MyAnnotation
{
String value() default "val0";
}
#Retention(RetentionPolicy.RUNTIME)
public #interface MyAnnotationArray
{
MyAnnotation[] value()
}
also tried against groovy 3.0.0-SNAPSHOT - the result is the same as for 2.4.11

Yes, Groovy has supported "repeatable" annotations for a long time even in Java 5 so long as retention policy was only SOURCE. This is what allows multiple #Grab statements for instance without the outer #Grapes container annotation. Being only retained in SOURCE makes them useful for AST transformations and within the Groovy compiler itself (and other source processors) but not really useful anywhere else. We don't currently support #Repeatable at all but plan to in a future version.

Related

How to apply `#POJO` to classes via config script?

I have a few classes which I'd like to keep as POJO. Manually annotating each of these would be troublesome, for both updating all current ones and adding future such classes.
I have a SourceAwareCustomizer able to identify all these classes. However I do not know how to apply the #POJO via the config script.
I tried ast(POJO), and I would get an error:
Provided class doesn't look like an AST #interface
I dug in the code a bit and found that #POJO is not an AST transformation (it's not annotated with #GroovyASTTransformationClass.
Is there a way to apply #POJO, or maybe a random annotation, to a class via the config script?
POJO is not an AST transformation.
Compare POJO source to ToString (for example). In POJO the GroovyASTTransformationClass annotation is missing..
I can't make #POJO working without #CompileStatic..
So, here is my try with groovy 4.0.1:
congig.groovy
import org.codehaus.groovy.ast.ClassNode
import org.codehaus.groovy.ast.AnnotationNode
import groovy.transform.stc.POJO
import groovy.transform.CompileStatic
withConfig(configuration) {
inline(phase:'SEMANTIC_ANALYSIS'){Object...args->
if(args.size()>2){
ClassNode cn = args[2]
if( cn.getSuperClass().name=="java.lang.Object" ) {
if( !cn.annotations.find{it.classNode.name==POJO.class.name} ) {
cn.addAnnotation( new AnnotationNode(new ClassNode(POJO.class)) )
//can't see how POJO could work without compile static in groovy 4.0.1
if( !cn.annotations.find{it.classNode.name==CompileStatic.class.name} )
cn.addAnnotation( new AnnotationNode(new ClassNode(CompileStatic.class)) )
}
}
println "class = $cn"
println "annotations = ${cn.getAnnotations()}"
}
}
}
A.groovy
class A{
String id
}
compile command line:
groovyc --configscript config.groovy A.groovy
generated class
public class A
{
private String id;
#Generated
public A() {}
#Generated
public String getId() {
return this.id;
}
#Generated
public void setId(final String id) {
this.id = id;
}
}

Mockito, channel output of void function to System.out?

I have this logging class in my legacy application,
I am trying to Mock it for testing and output all messages from "WriteLog" method to System.out
This is the class
public abstract class LoggingServicesWorker {
public abstract void WriteLog(ELogLevel arg0, int arg1,String arg2,String arg3);
}
This is what I did so far,
I am not sure how to write to System.out
Also How to tell the first argument to have any enum type (ELogLevel)?
LoggingServicesWorker logger = mock(LoggingServicesWorker.class);
Mockito.doNothing().when(logger).WriteLog(ELogLevel.DEBUG,anyInt(),anyString(),Mockito.eq(anyString()));
You can use Mockito#doAnswer for executing side-effects:
doAnswer((invocation -> {
System.out.println(Arrays.toString(invocation.getArguments()));
return null;
}))
.when(worker)
.WriteLog(any(ELogLevel.class), anyInt(), anyString(), anyString());
Standalone class with the default behavior for return values (and usable with Java 7):
class PrintArgsToStdoutAnswer implements Answer<Object> {
private final ReturnsEmptyValues defaultReturn = new ReturnsEmptyValues();
#Override
public Object answer(InvocationOnMock invocation) throws Throwable {
System.out.println(Arrays.toString(invocation.getArguments()));
return defaultReturn.answer(invocation);
}
}
...and used in the test method:
doAnswer(new PrintArgsToStdoutAnswer())...

Unexpected behaviour for Groovy 'with' method - variable assignment silently failed

I have the following code:
import groovy.transform.ToString
#ToString(includeNames = true)
class Simple {
String creditPoints
}
Simple simple = new Simple()
simple.with {
creditPoints : "288"
}
println simple
Clearly, I made a mistake here with creditPoints : "288". It should have been creditPoints = "288".
I expected Groovy to fail at the runtime saying that I made a mistake and I should have used creditPoints = "288"but clearly it did not.
Since it did not fail then what did Groovy do with the closure I created?
From the Groovy compiler perspective, there is no mistake in your closure code. The compiler sees creditPoints : "288" as labeled statement which is a legal construction in the Groovy programming language. As the documentation says, label statement does not add anything to the resulting bytecode, but it can be used for instance by AST transformations (Spock Framework uses it heavily).
It becomes more clear and easy to understand if you format code more accurately to the label statement use case, e.g
class Simple {
String creditPoints
static void main(String[] args) {
Simple simple = new Simple()
simple.with {
creditPoints:
"288"
}
println simple
}
}
(NOTE: I put your script inside the main method body to show you its bytecode representation in the next section.)
Now when we know how compiler sees this construction, let's take a look and see what does the final bytecode look like. To do this we will decompile the .class file (I use IntelliJ IDEA for that - you simply open .class file in IDEA and it decompiles it for you):
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
import groovy.lang.Closure;
import groovy.lang.GroovyObject;
import groovy.lang.MetaClass;
import groovy.transform.ToString;
import org.codehaus.groovy.runtime.DefaultGroovyMethods;
import org.codehaus.groovy.runtime.GeneratedClosure;
import org.codehaus.groovy.runtime.InvokerHelper;
#ToString
public class Simple implements GroovyObject {
private String creditPoints;
public Simple() {
MetaClass var1 = this.$getStaticMetaClass();
this.metaClass = var1;
}
public static void main(String... args) {
Simple simple = new Simple();
class _main_closure1 extends Closure implements GeneratedClosure {
public _main_closure1(Object _outerInstance, Object _thisObject) {
super(_outerInstance, _thisObject);
}
public Object doCall(Object it) {
return "288";
}
public Object call(Object args) {
return this.doCall(args);
}
public Object call() {
return this.doCall((Object)null);
}
public Object doCall() {
return this.doCall((Object)null);
}
}
DefaultGroovyMethods.with(simple, new _main_closure1(Simple.class, Simple.class));
DefaultGroovyMethods.println(Simple.class, simple);
Object var10000 = null;
}
public String toString() {
StringBuilder _result = new StringBuilder();
Boolean $toStringFirst = Boolean.TRUE;
_result.append("Simple(");
if ($toStringFirst == null ? false : $toStringFirst) {
Boolean var3 = Boolean.FALSE;
} else {
_result.append(", ");
}
if (this.getCreditPoints() == this) {
_result.append("(this)");
} else {
_result.append(InvokerHelper.toString(this.getCreditPoints()));
}
_result.append(")");
return _result.toString();
}
public String getCreditPoints() {
return this.creditPoints;
}
public void setCreditPoints(String var1) {
this.creditPoints = var1;
}
}
As you can see, your closure used with the with method is represented as an inner _main_closure1 class. This class extends Closure class, and it implements GeneratedClosure interface. The body of the closure is encapsulated in public Object doCall(Object it) method. This method only returns "288" string, which is expected - the last statement of the closure becomes a return statement by default. There is no label statement in the generated bytecode, which is also expected as labels get stripped at the CANONICALIZATION Groovy compiler phase.

How to mock System.getenv() in JUnit [Powermock + Parameterized]

How to mock "System.getenv("...")" in JUnit.
Currently I am doing:
#RunWith(Parameterized.class)
#PowerMockRunnerDelegate(PowerMockRunner.class)
#PrepareForTest(System.class)
public class TestClass extends BaseTest {
public TestClass(String testCase) {
this.testCase = testCase;
}
#Before
#Override
public final void initTable() throws Throwable {
super.initTable();
PowerMockito.mockStatic(System.class);
PowerMockito.when(System.getenv("ENV_VAR1")).thenReturn("1234");
}
...
}
I am using both PowerMock and Parameterizedrunner.
I am getting below exception for line:
PowerMockito.when(System.getenv("ENV_VAR1")).thenReturn("1234");
Exception:
org.mockito.exceptions.base.MockitoException:
'afterPropertiesSet' is a *void method* and it *cannot* be stubbed with a *return value*!
Voids are usually stubbed with Throwables:
doThrow(exception).when(mock).someVoidMethod();
***
Use the #RunWith(PowerMockRunner.class) annotation at the class-level of the test case.
Use the #PrepareForTest({ClassThatCallsTheSystemClass.class}) annotation at the class-level of the test case.
Example with using EasyMock
public class SystemClassUser {
public String performEncode() throws UnsupportedEncodingException {
return URLEncoder.encode("string", "enc");
}
}
And test
#RunWith(PowerMockRunner.class)
#PrepareForTest( { SystemClassUser.class })
public class SystemClassUserTest {
#Test
public void assertThatMockingOfNonFinalSystemClassesWorks() throws Exception {
mockStatic(URLEncoder.class);
expect(URLEncoder.encode("string", "enc")).andReturn("something");
replayAll();
assertEquals("something", new SystemClassUser().performEncode());
verifyAll();
}
}
From:
https://github.com/powermock/powermock/wiki/MockSystem
So, you should add a class that uses the System.getenv, not the System class to #PrepareForTest.
This post explains why it should be done in such way.
Also, I'd like to recommend to use the System Rules library for your case. It has a good way to stub environment variables. PowerMock modifies a class byte code, so it makes test slowly. And even if it not modify a class it at least read class from disk.

JukitoRunner, bind mock of final class

How to bind mock of final class in Jukito ?
For example :
public final class SomeFinalClass(){
public SomeFinalClass(String someString){
}
}
//Testing class
#Runwith(JukitoRunner.class)
public class TestingClass(){
#Inject
private SomeFinalClass someFinalClassMock;
public static class TestModule extends JukitoModule {
#Override
protected void configureTest() {
// bind(SomeClient.class).in(TestSingleton.class);
}
#Provides
public SomeFinalClass getSomkeFinalClass() {
return Mokito.mock(SomeFinalClass.class); //throws error
}
}
}
Is there a way i can use PowerMockito with JukitoRunner ?
You can mock a final class if you're using Mockito 2. From Mockito 2 Wiki:
Mocking of final classes and methods is an incubating, opt-in feature. It uses a combination of Java agent instrumentation and subclassing in order to enable mockability of these types. As this works differently to our current mechanism and this one has different limitations and as we want to gather experience and user feedback, this feature had to be explicitly activated to be available ; it can be done via the mockito extension mechanism by creating the file src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker containing a single line: mock-maker-inline.
After you created this file, Mockito will automatically use this new engine and one can do :
final class FinalClass {
final String finalMethod() { return "something"; }
}
FinalClass concrete = new FinalClass();
FinalClass mock = mock(FinalClass.class);
given(mock.finalMethod()).willReturn("not anymore");
assertThat(mock.finalMethod()).isNotEqualTo(concrete.finalMethod());

Resources