lishunli / powermock

Automatically exported from code.google.com/p/powermock
Apache License 2.0
0 stars 0 forks source link

Test with PowerMock fails when run on java 1.7.0_65 #504

Open GoogleCodeExporter opened 9 years ago

GoogleCodeExporter commented 9 years ago
What steps will reproduce the problem?
- Have a unit test annotated with @RunWith(PowerMockRunner.class) 
@PrepareForTest(MyEndpoint.class)
   and use mockStatic(MyEndpoint.class); in the setUp method of the test.

What is the expected output? What do you see instead?

I would expect that the test runs normally and without error as it did before 
with older java versions (1.7.0_55 works).

Instead the test fails with the following stacktrace:
java.lang.VerifyError: Bad <init> method call from inside of a branch Exception 
Details:   Location:     
net/sample/api/endpoint/AbstractB2CJaxRsEndpoint.<init>(Lorg/powermock/core/Indi
cateReloadClass;)V @41: invokespecial   Reason:     Error exists in the 
bytecode   Bytecode:     0000000: 2a2b 4e4d 1210 b800 1604 bd00 0d59 032d     
0000010: 5313 0126 b800 1bb8 0021 3a05 1905 b200     0000020: 25a5 000e 2a01 
c000 27b7 002a a700 0a2c     0000030: 2db7 002a 0157 b1                         
Stackmap Table:     
full_frame(@47,{UninitializedThis,Object[#39],UninitializedThis,Object[#39],Top,
Object[#13]},{})     
full_frame(@54,{Object[#2],Object[#39],Object[#2],Object[#39],Top,Object[#13]},{
}) 
java.lang.VerifyError: Bad <init> method call from inside of a branch
Exception Details:
  Location:
    net/sample/api/endpoint/AbstractB2CJaxRsEndpoint.<init>(Lorg/powermock/core/IndicateReloadClass;)V @41: invokespecial
  Reason:
    Error exists in the bytecode
  Bytecode:
    0000000: 2a2b 4e4d 1210 b800 1604 bd00 0d59 032d
    0000010: 5313 0126 b800 1bb8 0021 3a05 1905 b200
    0000020: 25a5 000e 2a01 c000 27b7 002a a700 0a2c
    0000030: 2db7 002a 0157 b1                      
  Stackmap Table:
    full_frame(@47,{UninitializedThis,Object[#39],UninitializedThis,Object[#39],Top,Object[#13]},{})
    full_frame(@54,{Object[#2],Object[#39],Object[#2],Object[#39],Top,Object[#13]},{})

    at java.lang.Class.getDeclaredConstructors0(Native Method)
    at java.lang.Class.privateGetDeclaredConstructors(Class.java:2532)
    at java.lang.Class.getDeclaredConstructors(Class.java:1901)
    at org.mockito.internal.creation.jmock.ClassImposterizer.setConstructorsAccessible(ClassImposterizer.java:75)
    at org.mockito.internal.creation.jmock.ClassImposterizer.imposterise(ClassImposterizer.java:70)
    at org.powermock.api.mockito.internal.mockcreation.MockCreator.createMethodInvocationControl(MockCreator.java:110)
    at org.powermock.api.mockito.internal.mockcreation.MockCreator.mock(MockCreator.java:60)
    at org.powermock.api.mockito.PowerMockito.mockStatic(PowerMockito.java:70)
    at net.sample.api.endpoint.contacting.communication.peer.CommunicationTest.setUp(CommunicationTest.java:82)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at org.junit.internal.runners.MethodRoadie.runBefores(MethodRoadie.java:132)
    at org.junit.internal.runners.MethodRoadie.runBeforesThenTestThenAfters(MethodRoadie.java:95)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.executeTest(PowerMockJUnit44RunnerDelegateImpl.java:296)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit49RunnerDelegateImpl$PowerMockJUnit49MethodRunner.executeTestInSuper(PowerMockJUnit49RunnerDelegateImpl.java:116)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit49RunnerDelegateImpl$PowerMockJUnit49MethodRunner.executeTest(PowerMockJUnit49RunnerDelegateImpl.java:77)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.runBeforesThenTestThenAfters(PowerMockJUnit44RunnerDelegateImpl.java:284)
    at org.junit.internal.runners.MethodRoadie.runTest(MethodRoadie.java:86)
    at org.junit.internal.runners.MethodRoadie.run(MethodRoadie.java:49)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.invokeTestMethod(PowerMockJUnit44RunnerDelegateImpl.java:209)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.runMethods(PowerMockJUnit44RunnerDelegateImpl.java:148)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$1.run(PowerMockJUnit44RunnerDelegateImpl.java:122)
    at org.junit.internal.runners.ClassRoadie.runUnprotected(ClassRoadie.java:33)
    at org.junit.internal.runners.ClassRoadie.runProtected(ClassRoadie.java:45)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.run(PowerMockJUnit44RunnerDelegateImpl.java:120)
    at org.powermock.modules.junit4.common.internal.impl.JUnit4TestSuiteChunkerImpl.run(JUnit4TestSuiteChunkerImpl.java:101)
    at org.powermock.modules.junit4.common.internal.impl.AbstractCommonPowerMockRunner.run(AbstractCommonPowerMockRunner.java:53)
    at org.powermock.modules.junit4.PowerMockRunner.run(PowerMockRunner.java:53)
    at org.apache.maven.surefire.junit4.JUnit4Provider.execute(JUnit4Provider.java:249)
    at org.apache.maven.surefire.junit4.JUnit4Provider.executeTestSet(JUnit4Provider.java:142)
    at org.apache.maven.surefire.junit4.JUnit4Provider.invoke(JUnit4Provider.java:113)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at org.apache.maven.surefire.util.ReflectionUtils.invokeMethodWithArray(ReflectionUtils.java:189)
    at org.apache.maven.surefire.booter.ProviderFactory$ProviderProxy.invoke(ProviderFactory.java:165)
    at org.apache.maven.surefire.booter.ProviderFactory.invokeProvider(ProviderFactory.java:85)
    at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:104)
    at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:75)

What version of the product are you using? On what operating system?
PowerMock 1.5.5 on MacOSX or Linux

Please provide any additional information below.

Original issue reported on code.google.com by begul1234 on 17 Jul 2014 at 8:28

GoogleCodeExporter commented 9 years ago
Most likely, it's a JDK bug

Root cause(?):

https://bugs.openjdk.java.net/browse/JDK-8051012

Related issues:

https://jira.codehaus.org/browse/GROOVY-6951?page=com.atlassian.jira.plugin.syst
em.issuetabpanels:all-tabpanel

http://zeroturnaround.com/forums/topic/verifyerror-bad-method-call-from-inside-o
f-a-branch/

Original comment by LifusExi...@gmail.com on 25 Jul 2014 at 10:27

GoogleCodeExporter commented 9 years ago
I believe the original bug was patched in 1.7's 67 patch, but the bug still 
seems to exist with PowerMock...

Original comment by JDever...@gmail.com on 13 Aug 2014 at 8:34

GoogleCodeExporter commented 9 years ago
This is a show-stopper for our use of PowerMock. Are there plans on the 
horizon? or is the recommendation to move to a different test library?

Original comment by awo...@fedora-commons.org on 19 Aug 2014 at 1:29

GoogleCodeExporter commented 9 years ago
I wonder if PowerMock needs to be rebuilt with a newer JDK version.

Original comment by epa...@gmail.com on 19 Aug 2014 at 8:03

GoogleCodeExporter commented 9 years ago
There is a work-around for this issue: run the junit tests with a '-noverify' 
jvm argument

Original comment by thinking...@gmail.com on 20 Aug 2014 at 9:47

GoogleCodeExporter commented 9 years ago
I added a comment to https://issues.jboss.org/browse/JASSIST-228 that suggest 
another workaround for this problem. (unfortunatelly they are maintaining their 
server currently and i cannot quote myself from there)

However, if you still feel that you need to have your java class file verifyed 
(hence -noverify is out for you) you can use -XX:-UseSplitVerifier option when 
you start your tests.

And if you are like me, and start your tests with maven, then you need to add 
something similar to your pom.xml
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.10</version>
                <configuration>
                    <argLine>-XX:-UseSplitVerifier</argLine>
                </configuration>
            </plugin>
        </plugins>
    </build>

Original comment by lauri.va...@gmail.com on 21 Aug 2014 at 12:21

GoogleCodeExporter commented 9 years ago
I'm not sure if there's anything I can do from the PowerMock side to improve 
this?

Original comment by johan.ha...@gmail.com on 25 Aug 2014 at 6:00

GoogleCodeExporter commented 9 years ago
Issue 505 has been merged into this issue.

Original comment by johan.ha...@gmail.com on 25 Aug 2014 at 6:01

GoogleCodeExporter commented 9 years ago
You must avoid branching before a super call.

You could change the ExprEditor in the MainMockTransformer Line 269-273 to use 
no branching. Or use branching only if really needed and verify it. (I don't 
know how to call the JVM verifier, using CtClasses 
.toClass().getDeclaredConstructors() would throw a Exception) From thereon you 
could show a nice error message explaining the details.

Original comment by f...@noidea.de on 27 Aug 2014 at 1:06

GoogleCodeExporter commented 9 years ago
Could you help out and provide a pull request for this? That would be great.

Original comment by johan.ha...@gmail.com on 27 Aug 2014 at 5:34

GoogleCodeExporter commented 9 years ago
Hmm I really would like to solve this but I'm not sure how to solve this 
without branching..

Original comment by johan.ha...@gmail.com on 28 Aug 2014 at 11:39

GoogleCodeExporter commented 9 years ago
Just disable constructor mocking if 

        CtClass ctClass = ClassPool.getDefault().makeClass("TestForBadOperandWhenInvokingInit");

        //      java.lang.VerifyError: Bad operand type when invoking <init>
        ctClass.addConstructor(CtNewConstructor.make(new CtClass[0], new CtClass[0], "{if (1 != 2){ super();}}", ctClass));
        final Class<?> clazz = ctClass.toClass();

        clazz.getDeclaredConstructors();

throws an Exception and log it out. There is already a fix commited for the 
next JDK version, so this bug should not haunt us for long.

Original comment by f...@noidea.de on 28 Aug 2014 at 12:18

GoogleCodeExporter commented 9 years ago
I don't believe that this is a bug in the JDK. Oracle has decided to make their 
byte code verification rules stricter-details here 
http://www.takipiblog.com/oracles-latest-java-8-update-broke-your-tools-how-did-
it-happen/.

I also don't believe that there will be a fix in future versions of java as it 
is not a bug it is a feature. Oracle Java 8u20 still has this "feature".

The suggestion to use the SplitVerifier may work in Java 7 but I believe that 
the SplitVerifier has been removed in java 8. There is the option to turn off 
verification completely for tests (ie -noverify) but the confidence is lost 
that your code will work in production without this option and I don't think 
disabling bytecode verification in production is a great idea.

The OpenJDK team may of reverted the change but I don't think oracle will be so 
nice :)

The only real option is to fix the offending PowerMock class.

Original comment by matt.mil...@gmail.com on 29 Aug 2014 at 5:07

GoogleCodeExporter commented 9 years ago
If you're right we need to figure out a way to fix it. How it works now is that 
before the call to super the MockGateway is invoked to decide whether or not to 
call the super method or call another constructor (generated by PowerMock). 
This is used for constructor suppression (suppress(constructor(X.class))) if I 
remember it correctly. All method invocations, field invocations etc works in 
the same way (they all ask the MockGateway if they should continue or replace 
the call with a mock). For constructors I'm not sure how to solve this unless 
we insert this if statement (which I believe is the cause of the error) before 
the super call in the constructor byte-code. If you have any ideas for a 
workaround I would be really happy. 

A really ugly work-around would be to let the user somehow configure PowerMock 
not to byte-code manipulate constructors (if you don't need to use this 
functionality). That way I assume that some errors can go away but PowerMock 
will be left in a degraded state. Not sure if I want to go down this route.. 
However I've been thinking about implementing something like this before to 
speed up the byte-code manipulation (such as turing off field interception 
which is quite slow and not used very often afaik).

Original comment by johan.ha...@gmail.com on 29 Aug 2014 at 5:22

GoogleCodeExporter commented 9 years ago
Hey guys, any progress on this?
It is affecting us too.

Thanks.

Original comment by jam...@gmail.com on 16 Sep 2014 at 10:41

GoogleCodeExporter commented 9 years ago
ctClass.addConstructor(CtNewConstructor.make(new CtClass[0], new CtClass[0], 
"{if (1 != 2){ super();}}", ctClass));

Looks like this line makes new constructor with code. Right?

{if (1 != 2){ super();}}". Not sure why 1 != 2 check is needed but this is what 
is causing the problem. the first thing you have to invoke in the constructor 
is the super constructor. So probably it should be something like this 
{super(); if (1 != 2) {}}

Original comment by BinisBe...@gmail.com on 19 Sep 2014 at 12:25

GoogleCodeExporter commented 9 years ago
This might be the simplified version you could use to reproduce the error.. 
The original code is:

code.append("if(value == 
").append(MockGateway.class.getName()).append(".PROCEED) {");
code.append("   $_ = $proceed($$);");
code.append("} else {");
code.append("   $_ = 
").append(getCorrectReturnValueType(returnTypeAsCtClass)).append(";");
code.append("}}");

It is there to decide if another constructor should be called.. that code must 
be changed to call super() first. YOu would loose the ability of on demand 
constructor mocking.. or you would have to replace the mocked constructors 
during mocking.

Original comment by f...@noidea.de on 19 Sep 2014 at 12:29

GoogleCodeExporter commented 9 years ago
So I had a chat to some of of the developers here and I've learnt that the 
Oracle JDK is built on top of OpenJDK so any fixes made in OpenJDK will find 
their way through to the Oracle JDK. Therefore this "feature" has been reverted 
as per this issue https://bugs.openjdk.java.net/browse/JDK-8051012 and we just 
need to wait for the relevant version to be released. For java 8 it looks like 
8u20 b31 has it fixed, the latest public release is for 8u20 is b26 so the fix 
hasn't been released yet. I couldn't find a release date for build 31 anywhere.

Original comment by matt.mil...@gmail.com on 24 Sep 2014 at 1:30

GoogleCodeExporter commented 9 years ago
Thanks a lot for sharing this.

Original comment by johan.ha...@gmail.com on 24 Sep 2014 at 5:10

GoogleCodeExporter commented 9 years ago
As an FYI: this appears to no longer be an issue on Oracle JDK7u72.

Original comment by jrh...@gmail.com on 14 Oct 2014 at 8:48

GoogleCodeExporter commented 9 years ago
Thanks, good to know

Original comment by johan.ha...@gmail.com on 15 Oct 2014 at 5:12

GoogleCodeExporter commented 9 years ago
I'm still getting it with Oracle MacOS, java full version "1.7.0_72-b14"

Original comment by jeff.pa...@gmail.com on 15 Oct 2014 at 10:43

GoogleCodeExporter commented 9 years ago
That's weird - same OS and same build and update version here. I'm not sure 
what the difference is there.

Original comment by jrh...@gmail.com on 16 Oct 2014 at 1:13

GoogleCodeExporter commented 9 years ago
Perhaps you have different test scenarios? It works in some cases but fails in 
others?! But then it could be another issue altogether?

Original comment by johan.ha...@gmail.com on 16 Oct 2014 at 1:40

GoogleCodeExporter commented 9 years ago
[deleted comment]
GoogleCodeExporter commented 9 years ago
Oracle released 8U25 on October 14th and this jdk fixed this error for me.

Original comment by emla...@gmail.com on 16 Oct 2014 at 10:09

GoogleCodeExporter commented 9 years ago
Simply to give another voice, installing jdk1.7.0_72 fixed this for me as well.

Original comment by Cameron...@gmail.com on 23 Oct 2014 at 9:21

GoogleCodeExporter commented 9 years ago
For reference, the bug that is apparently fixed is:

http://bugs.java.com/view_bug.do?bug_id=8051012

It is referenced from this page:

http://www.oracle.com/technetwork/java/javase/2col/7u72-bugfixes-2298229.html

I presume the bug isn't marked as resolved, because update 72, is some weird 
sort of halfway release, where Oracle recommends you use update 71 unless you 
need fixes from 72:
http://www.oracle.com/technetwork/java/javase/7u72-relnotes-2296190.html

Original comment by japear...@agiledigital.com.au on 7 Nov 2014 at 6:07

GoogleCodeExporter commented 9 years ago
This issue is present for me I tried 72.
It works with the UseSplitVerifier option. But, that will be deprecated:
https://code.google.com/p/powermock/issues/detail?id=504

So, unless this is fixed, will have to migrate to a different framework. I'll 
take a look to the code later on to see if there is something I can help with.

Original comment by rubenale...@gmail.com on 18 Nov 2014 at 12:02

GoogleCodeExporter commented 9 years ago
fwiw - I was experiencing the issue on 7u71, but it is ok on 7u72.

Original comment by iamascr...@gmail.com on 4 Jan 2015 at 11:41

GoogleCodeExporter commented 9 years ago
7u72 fixed this for me.

Original comment by kguel...@gmail.com on 12 Jan 2015 at 11:37

GoogleCodeExporter commented 9 years ago
for Java SE 8u20
java version "1.8.0_20"
Java(TM) SE Runtime Environment (build 1.8.0_20-b26)

add the following to pom.xml
 <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.12.4</version>
                <configuration>
                    <argLine>-noverify</argLine>
                </configuration>
            </plugin>

It should work if you are using Java SE 8u25

java version "1.8.0_25"
Java(TM) SE Runtime Environment (build 1.8.0_25-b17)
Java HotSpot(TM) 64-Bit Server VM (build 25.25-b02, mixed mode)

Original comment by q...@cyngn.com on 20 Jan 2015 at 6:41

GoogleCodeExporter commented 9 years ago
For Java SE 8u25, is it working other except option -noverify ?

Original comment by chirag.o...@gmail.com on 24 Feb 2015 at 7:14