hprange / wounit

The WOUnit framework contains a set of utilities for testing WebObjects applications using JUnit 4.7 or later capabilities.
http://hprange.github.io/wounit/
Apache License 2.0
19 stars 18 forks source link

InvocationTargetException testing partial entities #35

Closed paulhoadley closed 10 years ago

paulhoadley commented 10 years ago

This is similar to the issue I reported in #32 (in that it occurs with the exact same test I described there), but it's a different looking stack trace now.

I may have contributed to some confusion here, in that I pulled the test described in #32 from my test suite altogether, and then described a different partials-related problem (whereby attributes added by the augmented entities weren't being recognised as merged into the base entity) which was completely fixed in the recent 1.2.3 release.

What I'm seeing now is an InvocationTargetException trying to test the base class at the framework level. I'll reproduce here the code from #32, as it's the same:

public class UserTest {
    @Rule
    public MockEditingContext ec = new MockEditingContext("Ident");

    @Test
    public void createUser() {
        User u = User.createUser(ec, "password", "username");
        confirm(u, canBeSaved());
        return;
    }
}

Worse, the problem is non-deterministic: sometimes this test will pass, sometimes it will fail. When it fails, this is the current stack trace:

com.webobjects.foundation.NSForwardException [java.lang.reflect.InvocationTargetException] null:java.lang.reflect.InvocationTargetException
    at com.webobjects.foundation._NSUtilities._explainInstantiationException(_NSUtilities.java:626)
    at com.webobjects.foundation._NSUtilities.instantiateObjectWithConstructor(_NSUtilities.java:665)
    at com.webobjects.eoaccess.EOEntityClassDescription.createInstanceWithEditingContext(EOEntityClassDescription.java:242)
    at com.webobjects.eoaccess.EOUtilities.createAndInsertInstance(EOUtilities.java:861)
    at com.wounit.rules.AbstractEditingContextRule$UnderTestFacade.create(AbstractEditingContextRule.java:72)
    at com.wounit.rules.AnnotationProcessor.createEOForType(AnnotationProcessor.java:49)
    at com.wounit.rules.AnnotationProcessor.initializeObject(AnnotationProcessor.java:172)
    at com.wounit.rules.AnnotationProcessor.process(AnnotationProcessor.java:205)
    at com.wounit.rules.AbstractEditingContextRule.before(AbstractEditingContextRule.java:182)
    at com.wounit.rules.MockEditingContext.before(MockEditingContext.java:147)
    at com.wounit.rules.AbstractEditingContextRule$1.evaluate(AbstractEditingContextRule.java:162)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:263)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:68)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:47)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:231)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:60)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:50)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:222)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:300)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
Caused by: java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
    at com.webobjects.foundation._NSUtilities.instantiateObjectWithConstructor(_NSUtilities.java:659)
    ... 24 more
Caused by: java.lang.NullPointerException
    at com.webobjects.eoaccess.EOModel.createPrototypeCache(EOModel.java:631)
    at com.webobjects.eoaccess.EOModel.prototypeAttributeNamed(EOModel.java:699)
    at com.webobjects.eoaccess.ERXModel.prototypeAttributeNamed(ERXModel.java:290)
    at com.webobjects.eoaccess.EOAttribute.<init>(EOAttribute.java:998)
    at com.webobjects.eoaccess.EOEntity.attributes(EOEntity.java:816)
    at com.webobjects.eoaccess.EOEntity.attributeNamed(EOEntity.java:789)
    at com.webobjects.eoaccess.EOEntity.classProperties(EOEntity.java:1098)
    at com.webobjects.eoaccess.EOEntity._propertyDictionaryInitializer(EOEntity.java:3321)
    at com.webobjects.eoaccess.EOEntity._newDictionaryForProperties(EOEntity.java:3667)
    at com.webobjects.eoaccess.EOEntityClassDescription._newDictionaryForProperties(EOEntityClassDescription.java:88)
    at com.webobjects.eocontrol.EOGenericRecord.__setClassDescription(EOGenericRecord.java:111)
    at com.webobjects.eocontrol.EOGenericRecord.__setClassDescription(EOGenericRecord.java:100)
    at com.webobjects.eocontrol.EOGenericRecord.<init>(EOGenericRecord.java:73)
    at er.extensions.eof.ERXGenericRecord.<init>(ERXGenericRecord.java:103)
    at er.extensions.partials.ERXPartialGenericRecord.<init>(ERXPartialGenericRecord.java:28)
    at net.logicsquad.access.model._User.<init>(_User.java:20)
    at net.logicsquad.access.model.User.<init>(User.java:14)
    ... 29 more

Note that that is different to the stack trace in #32.

So, to re-iterate, 1.2.3 completely solves the initialisation-related issue with merging of properties down to the base class. But the problem of testing just the base classes seems to persist.

Henrique, I'm going to send you my framework and its tests out of band in the hope you can pinpoint the problem quickly.

hprange commented 10 years ago

Are there any chances these tests are running in parallel?

paulhoadley commented 10 years ago

As we've subsequently discussed in email, no it's just a single test method. (Also, this is running within Eclipse—is there a way to set JUnit concurrency within Eclipse?) If you end up adding the locking mechanism you talked about elsewhere, I'll see if that has any effect, though presumably that would only help if parallel testing was the problem, and in this case it can't be, right?

hprange commented 10 years ago

I've reproduced it! I'm looking for a solution. I think the clue is in this log statement:

- Entity classDescription is not ERXEntityClassDescription: Login
- Entity classDescription is not ERXEntityClassDescription: User
- Entity classDescription is not ERXEntityClassDescription: UserRole

It appears only if the test throws the mentioned exception.

paulhoadley commented 10 years ago

Great news Henrique. Let me know if there's anything I can do to help.

hprange commented 10 years ago

I've found the root cause of the problem. Unfortunately, its a bug in Project Wonder. I'm not sure I can do something on WOUnit to workaround this problem.

I've added a few log messages to ERXModel and ERXModelGroup classes. The numbers in parenthesis are the hash code value for each object.

1. Add model erprototypes (322185831) to the default model group
2. Add model Control (429231097) to the default model group
3. Add model Ident (817101240) to the default model group
4. Refuse to add duplicate model Control (689037010) to the default model group
5. Refuse to add duplicate model Ident (1413202446) to the default model group
6. Load attribute prototype named from model Ident (817101240)
7. Load attribute prototype named from model Control (429231097)
8. Load attribute prototype named from model Ident (1413202446)

Pay attention to lines 5 and 8. At line 5, the model group refuses to load the model Ident with hash code 1413202446 because there's already one model Ident loaded. However, an attribute prototype is loaded from the Ident model 1413202446 at line 8 only if the test throws the mentioned exception. The line 8 doesn't appear when the test pass.

I have no idea why the prototype is loaded from the wrong model. In spite of that fact, I know how to fix the Wonder code to avoid the creation of duplicate models. I've created the pull request https://github.com/wocommunity/wonder/pull/551 as an attempt to solve this problem. Would you mind to try it?

paulhoadley commented 10 years ago

I will test it out, yes. Give me a couple of days.

paulhoadley commented 10 years ago

The ERXModelGroup patch in wocommunity/wonder#551 completely fixes this issue for me.

paulhoadley commented 10 years ago

I should have closed this back in March. The fix to ERXModelGroup referred to above completely resolves this for me. (I just updated Wonder to 6.1.2 locally, which fixed these tests on partial entities for me, which reminded me to go close this issue. Sorry it stayed open so long, there was no need.)