ahmaddarawshi / powermock

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

Calling another method in the same class from equals results in a StackOverflowError #88

Closed GoogleCodeExporter closed 9 years ago

GoogleCodeExporter commented 9 years ago
E.g. 
public class ClassA 
{

   @Override
       public int hashCode()
   {
       return getId();
   }

   public int getId()
   {
       return 3;
   }
}

Original issue reported on code.google.com by johan.ha...@gmail.com on 16 Jan 2009 at 5:40

GoogleCodeExporter commented 9 years ago
Fixed in trunk for version 1.2

Original comment by johan.ha...@gmail.com on 19 Jan 2009 at 8:43

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

Original comment by johan.ha...@gmail.com on 22 Jan 2009 at 1:42

GoogleCodeExporter commented 9 years ago
There seems to be a similar problem for classes implementing equals() as a final
method which calls another method of that class.
The attached example provokes a StackOverflowError when executed.

StackTrace:
java.lang.StackOverflowError
    at java.lang.reflect.Field.<init>(Field.java:103)
    at java.lang.reflect.Field.copy(Field.java:126)
    at java.lang.reflect.ReflectAccess.copyField(ReflectAccess.java:122)
    at sun.reflect.ReflectionFactory.copyField(ReflectionFactory.java:289)
    at java.lang.Class.copyFields(Class.java:2711)
    at java.lang.Class.getDeclaredFields(Class.java:1715)
    at org.powermock.reflect.internal.WhiteboxImpl.getField(WhiteboxImpl.java:188)
    at org.powermock.core.MockGateway.fieldCall(MockGateway.java:117)
    at powermocktest.ClassToMock.equals(PowerMockEqualsTest.java)
    at org.easymock.internal.ExpectedInvocation.matches(ExpectedInvocation.java:77)
    at org.easymock.internal.UnorderedBehavior.addActual(UnorderedBehavior.java:38)
    at org.easymock.internal.MocksBehavior.addActual(MocksBehavior.java:74)
    at org.easymock.internal.ReplayState.invokeInner(ReplayState.java:37)
    at org.easymock.internal.ReplayState.invoke(ReplayState.java:33)
    at org.easymock.internal.MockInvocationHandler.invoke(MockInvocationHandler.java:27)
    at org.easymock.internal.ObjectMethodsFilter.invoke(ObjectMethodsFilter.java:61)
    at
org.powermock.api.easymock.internal.signedsupport.SignedSupportingClassProxyFact
ory$1.intercept(SignedSupportingClassProxyFactory.java:125)
    at powermocktest.ClassToMock$$EnhancerByCGLIB$$8fa55cb1.equals(<generated>)
    at powermocktest.ClassToMock.equals(PowerMockEqualsTest.java:45)
    at org.easymock.internal.ExpectedInvocation.matches(ExpectedInvocation.java:77)
    at org.easymock.internal.UnorderedBehavior.addActual(UnorderedBehavior.java:38)
    at org.easymock.internal.MocksBehavior.addActual(MocksBehavior.java:74)
    at org.easymock.internal.ReplayState.invokeInner(ReplayState.java:37)
    at org.easymock.internal.ReplayState.invoke(ReplayState.java:33)
    at org.easymock.internal.MockInvocationHandler.invoke(MockInvocationHandler.java:27)
    at org.easymock.internal.ObjectMethodsFilter.invoke(ObjectMethodsFilter.java:61)
    at
org.powermock.api.easymock.internal.signedsupport.SignedSupportingClassProxyFact
ory$1.intercept(SignedSupportingClassProxyFactory.java:125)
...

Original comment by marcel.f...@googlemail.com on 15 Apr 2009 at 12:49

Attachments:

GoogleCodeExporter commented 9 years ago
I'll look into it asap, thanks!

Original comment by johan.ha...@gmail.com on 15 Apr 2009 at 1:51

GoogleCodeExporter commented 9 years ago
I've verified the problem and it's a bug. Thanks for finding this! We'll do our 
best
to solve it.

Original comment by johan.ha...@gmail.com on 16 Apr 2009 at 11:45

GoogleCodeExporter commented 9 years ago

Original comment by jan.kron...@gmail.com on 27 Apr 2009 at 4:21

GoogleCodeExporter commented 9 years ago

Original comment by jan.kron...@gmail.com on 27 Apr 2009 at 4:56

GoogleCodeExporter commented 9 years ago

Original comment by johan.ha...@gmail.com on 19 Sep 2009 at 7:41

GoogleCodeExporter commented 9 years ago
I'm facing a similar issue when trying to mock a final class. My test code is
something like this:

  Project project = PowerMock.createMock(Project.class);
  EasyMock.expect(project.getURL()).andReturn(url);
  PowerMock.replay(project);

Then the replay method throws a StackOverflowError:

java.lang.StackOverflowError
    at org.powermock.reflect.internal.WhiteboxImpl$2.run(WhiteboxImpl.java:1287)
    at org.powermock.reflect.internal.WhiteboxImpl$2.run(WhiteboxImpl.java:1284)
    at java.security.AccessController.doPrivileged(Native Method)
    at org.powermock.reflect.internal.WhiteboxImpl.getAllMethods(WhiteboxImpl.java:1284)
    at org.powermock.reflect.internal.WhiteboxImpl.getMethods(WhiteboxImpl.java:1526)
    at org.powermock.reflect.internal.WhiteboxImpl.getMethods(WhiteboxImpl.java:1564)
    at
org.powermock.reflect.internal.WhiteboxImpl.getBestMethodCandidate(WhiteboxImpl.
java:836)
    at org.powermock.core.MockGateway.doMethodCall(MockGateway.java:82)
    at org.powermock.core.MockGateway.methodCall(MockGateway.java:148)
    at ProjectSuperClass.identical(ProjectSuperClass.java)
    at Project.equals(Project.java:789)
    at org.easymock.internal.Invocation.equals(Invocation.java:95)

The problem lies on Easymock's relying on the equals() method defined in the 
class
being mocked:

    public boolean equals(Object o) {
        if (o == null || !o.getClass().equals(this.getClass()))
            return false;

        Invocation other = (Invocation) o;

        return this.mock.equals(other.mock) && this.method.equals(other.method)
                && this.equalArguments(other.arguments);
    }

Which in the case of Project is something like:

  public final boolean equals(Object o)
  {
    if ( o == null || o.getClass() != getClass() ) {
      return false;
    } else {
      return identical(o);
    }
  }

Because o.getClass() != getClass() fails (as o's class is the bytecode-enhanced
class), it calls method identical(), which recursively calls equals(). I think 
the
solution would be to not rely on equals() in this case, but in other mechanism 
like
System.identityHashCode:

        return System.identityHashcode(this.mock) ==
System.identityHashcode(other.mock)&& ...

-- Felipe

Original comment by felip...@gmail.com on 5 May 2010 at 5:52

GoogleCodeExporter commented 9 years ago
Hi, 

We're aware of this. I've actually made fixes in trunk which solves the 
issue(s) with 
Mockito making it possible to mock/suppress/stub final toString, equals and 
hashcode 
methods. However for EasyMock you'd have to modify the internals of EasyMock 
for this 
to work (just as you point out). Perhaps you could bring this up on the 
EasyMock 
mailinglist? Mockito doesn't have this issue so there must another way for them 
to do 
it.

/Johan

Original comment by johan.ha...@gmail.com on 7 May 2010 at 6:09

GoogleCodeExporter commented 9 years ago
Changing Easymock's Invocation to use System.identityHashMap() fixes it. I will 
try
to write a simpler test case (that does not rely on PowerMock) to reproduce the
issue, then submit a patch to Easymock.

Original comment by felip...@gmail.com on 7 May 2010 at 4:36

GoogleCodeExporter commented 9 years ago
Excellent!

Original comment by johan.ha...@gmail.com on 7 May 2010 at 5:43

GoogleCodeExporter commented 9 years ago

Original comment by johan.ha...@gmail.com on 22 Jul 2010 at 9:16

GoogleCodeExporter commented 9 years ago
Changing to a lower priority since this will hopefully be solved by the 
EasyMock team.

Original comment by johan.ha...@gmail.com on 8 Aug 2010 at 9:02

GoogleCodeExporter commented 9 years ago
We have a similar problem with StackOverflowError, using either PowerMock 
version 1.3.8 with mockito 1.8.4 or PowerMock version 1.4.5 with mockito 1.8.5.

Relevant lines from the stack trace:
java.lang.StackOverflowError
        at java.security.AccessController.doPrivileged(Native Method)
        at org.powermock.reflect.internal.WhiteboxImpl.getAllMethods(WhiteboxImpl.java:1626)
        at org.powermock.reflect.internal.WhiteboxImpl.getMethods(WhiteboxImpl.java:1909)
        at org.powermock.reflect.internal.WhiteboxImpl.getMethods(WhiteboxImpl.java:1952)
        at org.powermock.reflect.internal.WhiteboxImpl.getBestMethodCandidate(WhiteboxImpl.java:1056)
        at org.powermock.core.MockGateway.doMethodCall(MockGateway.java:89)
        at org.powermock.core.MockGateway.methodCall(MockGateway.java:168)
        at com.google.gwt.core.client.JavaScriptObject.equals(JavaScriptObject.java)
        at org.mockito.internal.invocation.InvocationMatcher.matches(InvocationMatcher.java:57)
        at org.mockito.internal.stubbing.InvocationContainerImpl.findAnswerFor(InvocationContainerImpl.java:72)
        at org.mockito.internal.MockHandler.handle(MockHandler.java:93)
        at org.powermock.api.mockito.internal.invocationcontrol.MockitoMethodInvocationControl.performIntercept(MockitoMethodInvocationControl.java:267)
        at org.powermock.api.mockito.internal.invocationcontrol.MockitoMethodInvocationControl.invoke(MockitoMethodInvocationControl.java:169)
        at org.powermock.core.MockGateway.doMethodCall(MockGateway.java:105)
        at org.powermock.core.MockGateway.methodCall(MockGateway.java:168)
        at com.google.gwt.core.client.JavaScriptObject.equals(JavaScriptObject.java)
...

We can also post source code if needed or try to minimize to a suitable test 
case.

As I read this bug, and according to comment 10,  it should have been fixed for 
mockito with the following commit:
http://code.google.com/p/powermock/source/detail?r=1294

which is also mentioned in the change log for PowerMock 1.3.8.

So is this fixed for any combination of PowerMock / Mockito? Has it been 
released?

If you need more info feel free to ask!

Thanks,
-fotos

Original comment by gfo...@gmail.com on 31 Aug 2010 at 9:15

GoogleCodeExporter commented 9 years ago
[deleted comment]
GoogleCodeExporter commented 9 years ago
[deleted comment]
GoogleCodeExporter commented 9 years ago
I've also been getting StackOverflowErrors when trying to mock a class with a 
final method. The class has a superclass that overrides equals(). If I remove 
the equals() implementation then the error does not occur and the test 
completes normally. I am using powermock 1.4.6 (also tried 1.4.5) with JUnit 
4.8.1, downloaded with maven:

   <dependency>
      <groupId>org.powermock</groupId>
      <artifactId>powermock-module-junit4</artifactId>
      <version>1.4.6</version>
      <scope>test</scope>
   </dependency>
   <dependency>
      <groupId>org.powermock</groupId>
      <artifactId>powermock-api-mockito</artifactId>
      <version>1.4.6</version>
      <scope>test</scope>
   </dependency>

Original comment by teevee...@hotmail.co.uk on 19 Oct 2010 at 3:31

GoogleCodeExporter commented 9 years ago
I have the same issue. Has the fix been released?

Original comment by linh.dan...@gmail.com on 24 Jan 2011 at 9:13

GoogleCodeExporter commented 9 years ago
I am having the same issue as well.  I am using PowerMock  1.4.9 with Mockito 
1.8.5.  Following is the stack trace:

java.lang.StackOverflowError
    at java.lang.ref.Reference.get(Reference.java:160)
    at java.lang.ref.SoftReference.get(SoftReference.java:93)
    at java.lang.Class.privateGetDeclaredMethods(Class.java:2421)
    at java.lang.Class.getDeclaredMethods(Class.java:1791)
    at org.powermock.reflect.internal.WhiteboxImpl$3.run(WhiteboxImpl.java:1608)
    at org.powermock.reflect.internal.WhiteboxImpl$3.run(WhiteboxImpl.java:1605)
    at java.security.AccessController.doPrivileged(Native Method)
    at org.powermock.reflect.internal.WhiteboxImpl.getAllMethods(WhiteboxImpl.java:1605)
    at org.powermock.reflect.internal.WhiteboxImpl.getMethods(WhiteboxImpl.java:1888)
    at org.powermock.reflect.internal.WhiteboxImpl.getMethods(WhiteboxImpl.java:1931)
    at org.powermock.reflect.internal.WhiteboxImpl.getBestMethodCandidate(WhiteboxImpl.java:1025)
    at org.powermock.core.MockGateway.doMethodCall(MockGateway.java:89)
    at org.powermock.core.MockGateway.methodCall(MockGateway.java:168)
    at net.sf.ehcache.Element.equals(Element.java)
    at org.mockito.internal.invocation.InvocationMatcher.matches(InvocationMatcher.java:57)
    at org.mockito.internal.stubbing.InvocationContainerImpl.findAnswerFor(InvocationContainerImpl.java:72)
    at org.mockito.internal.MockHandler.handle(MockHandler.java:93)
    at org.powermock.api.mockito.internal.invocationcontrol.MockitoMethodInvocationControl.performIntercept(MockitoMethodInvocationControl.java:291)
    at org.powermock.api.mockito.internal.invocationcontrol.MockitoMethodInvocationControl.invoke(MockitoMethodInvocationControl.java:193)
    at org.powermock.core.MockGateway.doMethodCall(MockGateway.java:105)
    at org.powermock.core.MockGateway.methodCall(MockGateway.java:168)
    ...
    at net.sf.ehcache.Element.equals(Element.java)
    at org.mockito.internal.invocation.InvocationMatcher.matches(InvocationMatcher.java:57)
    at org.mockito.internal.stubbing.InvocationContainerImpl.findAnswerFor(InvocationContainerImpl.java:72)

Original comment by forthw...@gmail.com on 10 Aug 2011 at 8:31

GoogleCodeExporter commented 9 years ago
I have the same issue. Has the fix been released? Where can I get it?

Original comment by michael....@gmail.com on 8 Sep 2011 at 9:04

GoogleCodeExporter commented 9 years ago
Unfortunately not. I'd really appreciate some help with this.

Original comment by johan.ha...@gmail.com on 8 Sep 2011 at 10:01

GoogleCodeExporter commented 9 years ago
ok it works, if you remove the "final" from the "equals"-method

Original comment by michael....@gmail.com on 8 Sep 2011 at 1:21

GoogleCodeExporter commented 9 years ago
But, why?

Original comment by michael....@gmail.com on 8 Sep 2011 at 1:21

GoogleCodeExporter commented 9 years ago
When I wrote this, only God and I understood what I was doing. Now, God only 
knows.

Original comment by johan.ha...@gmail.com on 8 Sep 2011 at 2:02

GoogleCodeExporter commented 9 years ago
Hi guys,

Still reproducible with Mockito 1.9.0 and Powermock 1.4.11. Actually, it's 
reproducible with any final equals().

Regards,
Daniel

Original comment by gloeckne...@gmail.com on 25 Jan 2012 at 11:51

GoogleCodeExporter commented 9 years ago
There has been no work on this from my side. Please help out.

Original comment by johan.ha...@gmail.com on 25 Jan 2012 at 12:11

GoogleCodeExporter commented 9 years ago
I am having this issue as well - any plans to solve it in near future?

Original comment by kals...@gmail.com on 2 Apr 2012 at 3:53

GoogleCodeExporter commented 9 years ago
I would really appreciate some help with this issue. Provide a patch and I'll 
include it in the next release. 

Original comment by johan.ha...@gmail.com on 2 Apr 2012 at 5:38

GoogleCodeExporter commented 9 years ago
Hi all,

I've attached some simple code to parse a .class file and generate a new .class 
file with all final Opcodes removed.

I'm not really familiar with how ClassLoaders work but I guess this could be 
adapted to modify the target classes at runtime removing final from only the 
equals method.

Not sure if this is helpful or not.  For now I'm just parsing my 3rd party 
libraries class files with it so that I can run the tests.

Karle

Original comment by karlek...@gmail.com on 15 May 2012 at 11:48

Attachments:

GoogleCodeExporter commented 9 years ago
Oops, chopped off imports from above:

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;

import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;

Dependencies: asm-4.0.jar

Original comment by karlek...@gmail.com on 15 May 2012 at 11:49

GoogleCodeExporter commented 9 years ago
Also seeing this issue: Mockito 1.8.5 & Powermocks 1.4.9.  I don't have control 
over the code with the offending final equals.. any suggestions on a workaround 
to mock it..?

Original comment by mark.dav...@gmail.com on 2 Jul 2012 at 4:41

GoogleCodeExporter commented 9 years ago
I think I might have found a fix for that. 

The idea is to add an identity check to 
org.mockito.internal.invocation.InvocationMatcher.matches(Invocation). The code 
here looks as follows:

  public boolean matches(Invocation actual) {
      return invocation.getMock().equals(actual.getMock()) && 
hasSameMethod(actual)
      && new ArgumentsComparator().argumentsMatch(this, actual);
  }

It seems to me that calling equals on invocation.getMock() ends up in the same 
method and thereby triggers the StackOverflow. My solution to that would be to 
make an identity comparision. When the identity is the same the objects must be 
equal too, at least for common java standards. When I change the matches-method 
to the following:

public boolean matches(Invocation actual) {
return (
invocation.getMock() == actual.getMock()
|| invocation.getMock().equals(actual.getMock())
) && hasSameMethod(actual) && new ArgumentsComparator().argumentsMatch(this, 
actual);
}

My test completes

I cannot really tell if this has any unwanted side-effects, but as I just 
wrote, according to java standards it should not. 

Original comment by peer.har...@gmail.com on 10 Apr 2014 at 4:24

GoogleCodeExporter commented 9 years ago
Is this code change required inside Mockito? Is there anything we can do from 
the PowerMock side?

Original comment by johan.ha...@gmail.com on 11 Apr 2014 at 9:12

GoogleCodeExporter commented 9 years ago
Yes I think so. You could identify that problem in the MockGateway class and 
perform a workaround there. I attach an updated version with a fix. It uses 
Mockito's MethodGuru to identify the situation and then simply returns PROCEED 
which results in a call to the real method.
Since AFAIK it is not possible to Mock equals-Methods with mockito, that should 
be the desired behavior. But again I am not an expert on PowerMock's internal 
concepts...

Original comment by peer.har...@gmail.com on 29 Apr 2014 at 7:53

Attachments:

GoogleCodeExporter commented 9 years ago
Could you please provide this patch as a pull request on github??

Original comment by johan.ha...@gmail.com on 4 Jun 2014 at 6:07

GoogleCodeExporter commented 9 years ago
I was able to work around this issue by setting:

MockGateway.MOCK_STANDARD_METHODS = false;

It makes MockGateway not mock toString, hashcode, and equals.

Original comment by rsroman...@gmail.com on 13 Jun 2014 at 7:16

GoogleCodeExporter commented 9 years ago
This should now have been fixed in master. Published a new snapshot, depend on 
version 1.5.6-SNAPSHOT to try it out after having added the following repo:

<repositories>
        <repository>
            <id>sonatype</id>
            <url>https://oss.sonatype.org/content/repositories/snapshots/</url>
            <snapshots />
        </repository>
</repositories>

Original comment by johan.ha...@gmail.com on 10 Jul 2014 at 6:34

GoogleCodeExporter commented 9 years ago

Original comment by johan.ha...@gmail.com on 10 Jul 2014 at 6:35

GoogleCodeExporter commented 9 years ago
I could still see this issue. I can see that the status has been updated to 
Fixed. But can you please provide the Mockito and PowerMock versions post which 
we will not see this issue?

Original comment by siddhart...@gmail.com on 23 Sep 2014 at 4:00

GoogleCodeExporter commented 9 years ago
Try the latest version of PowerMock and Mockito

Original comment by johan.ha...@gmail.com on 23 Sep 2014 at 5:14