Bug 337935 - Test failures when run as an IDE (org.eclipse.sdk.ide)
Summary: Test failures when run as an IDE (org.eclipse.sdk.ide)
Status: VERIFIED FIXED
Alias: None
Product: JDT
Classification: Eclipse Project
Component: Core (show other bugs)
Version: 3.7   Edit
Hardware: PC Windows XP
: P3 normal (vote)
Target Milestone: 3.8 M2   Edit
Assignee: Stephan Herrmann CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2011-02-23 00:47 EST by Jay Arthanareeswaran CLA
Modified: 2011-09-13 15:21 EDT (History)
4 users (show)

See Also:


Attachments
sketch of the simplest fix (3.39 KB, patch)
2011-08-14 13:44 EDT, Stephan Herrmann CLA
no flags Details | Diff
alternative fix (2.39 KB, patch)
2011-08-17 09:55 EDT, Stephan Herrmann CLA
no flags Details | Diff
improved fix (3.67 KB, patch)
2011-09-04 17:50 EDT, Stephan Herrmann CLA
no flags Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Jay Arthanareeswaran CLA 2011-02-23 00:47:38 EST
The following tests are failing when the Program to Run is pointed to:
Run a product -> org.eclipse.sdk.ide

ClassFileTests#testWorkingCopy01
ClassFileTests#testWorkingCopy02
ClassFileTests#testWorkingCopy05
ClassFileTests#testWorkingCopy10

The failures don't occur when run as Headless application.
Comment 1 Stephan Herrmann CLA 2011-03-12 08:29:10 EST
I was affected by this, too, so I did some delta debugging and this is the
point where I see behavior starting to differ:


DefaultWorkingCopyOwner.createBuffer(ICompilationUnit) line: 32	
ClassFileWorkingCopy.openBuffer(IProgressMonitor, Object) line: 84	
ClassFileWorkingCopy(CompilationUnit).buildStructure(OpenableElementInfo, IProgressMonitor, Map, IResource) line: 116	
ClassFileWorkingCopy(Openable).generateInfos(Object, HashMap, IProgressMonitor) line: 258	
ClassFileWorkingCopy(JavaElement).openWhenClosed(Object, IProgressMonitor) line: 518	
BecomeWorkingCopyOperation.executeOperation() line: 38	
BecomeWorkingCopyOperation(JavaModelOperation).run(IProgressMonitor) line: 728	
BecomeWorkingCopyOperation(JavaModelOperation).runOperation(IProgressMonitor) line: 788	
ClassFile.getWorkingCopy(WorkingCopyOwner, IProgressMonitor) line: 571	
ClassFileTests.testWorkingCopy01() line: 1238	

In createBuffer, in the good case we have no primaryBufferProvider and
proceed to super.createBuffer(), in the bad case, however, we do have one,
an instance of JavaPlugin$2, and that unfortunate instance starts creating
a Document for the jar file! As a consequence later the content of the 
jar file (undecoded zip!) is fed verbatim into the SourceElementParser,
with expectable havoc.

So if the jdt.ui has already started things go awry. I no longer think that
just the test is broken, but we seem to have a real bug here.
Someone has to check if the working copy is a ClassFileWorkingCopy.
Since that class is internal, jdt.ui won't be able to. So the easiest fix
would be this one-line change in DefaultWorkingCopyOwner:

 if (this.primaryBufferProvider != null && !(workingCopy instanceof ClassFileWorkingCopy)) return this.primaryBufferProvider.createBuffer(workingCopy);

I have no idea what else this would break, though.
Comment 2 Stephan Herrmann CLA 2011-04-03 15:04:08 EDT
Alternatively, would JDT/UI have a chance to determine 
if an ICompilationUnit actually represents a class file, 
so they can avoid creating a bogus Document for those?

Perhaps by checking the underlying resource for a ".class" suffix?
Comment 3 Stephan Herrmann CLA 2011-08-14 12:35:39 EDT
Given that the tested behavior is effectively broken in the IDE and
since the Object Teams test suite is constantly affected by this I'd like
to help resolve this bug. 

I don't see how a DocumentAdapter for a ClassFileWorkingCopy could ever
make any sense? So wouldn't it indeed be best if UI's JavaPlugin
avoids creating these bogus DocumentAdapters? (see JavaPlugin.java:368).

Question remains: how can UI determine if a ICompilationUnit represents
a class file? What would be cheaper, adding isBinary() to ICompilationUnit
or letting ClassFileWorkingCopy implement a new public marker interface
IClassFileWorkingCopy or just IClassFile to check against?
Comment 4 Stephan Herrmann CLA 2011-08-14 13:44:24 EDT
Created attachment 201477 [details]
sketch of the simplest fix

I did briefly play with letting ClassFileWorkingCopy implement IClassFile,
which looked convincing at first, but it seems the hierarchies of
  ICompilationUnit
    CompilationUnit
      ClassFileWorkingCopy
and
  IClassFile
     ClassFile
cannot easily be made more symmetric, so I gave up on that.

However, the attached patch (combined Core & UI) seems to do the trick 
 - using indeed a new marker interface IClassFileWorkingCopy.
Comment 5 Dani Megert CLA 2011-08-15 05:15:09 EDT
The patch is a no go. At the API level a working copy is for CUs if someone calls org.eclipse.jdt.core.WorkingCopyOwner.createBuffer(ICompilationUnit) for something else then an ICompiltaionUnit then this is just wrong. If JDT Core does this internally, then JDT Core must also internally protect itself.

Having said that: AFAIK only JDT Core tests are affected. Hence the simplest fix is to call WorkingCopyOwner.setPrimaryBufferProvider(null) during test setup.
Comment 6 Stephan Herrmann CLA 2011-08-15 19:42:22 EDT
(In reply to comment #5)
> The patch is a no go. At the API level a working copy is for CUs if someone
> calls org.eclipse.jdt.core.WorkingCopyOwner.createBuffer(ICompilationUnit) for
> something else then an ICompiltaionUnit then this is just wrong.

Please help me understand what exactly is wrong. 
The tests show that it is possible that the argument to createBuffer
is a ClassFileWorkingCopy, which is a subtype of ICompilationUnit.
When wrapped in a DocumentAdapter this causes a text buffer to be created 
from the binary file content (we see a jar), causing havoc down the road.

Are you saying the existence of class ClassFileWorkingCopy as a subclass
of CompilationUnit is a bug?
Or is it wrong to pass such an instance to createBuffer()?
(I don't see this prohibited by the API doc).
Maybe we have an impossible/inconsistent API contract here?

> If JDT Core
> does this internally, then JDT Core must also internally protect itself.

I.e., must ensure that instances of ClassFileWorkingCopy *never* leak to 
any client? In that case I wouldn't know how 
IClassFile.getWorkingCopy(WorkingCopyOwner, IProgressMonitor) should be
implemented.
 
> Having said that: AFAIK only JDT Core tests are affected. Hence the simplest
> fix is to call WorkingCopyOwner.setPrimaryBufferProvider(null) during test
> setup.

I'm not sure why you think so. Do these tests perform anything that
can never happen in real life? They look pretty unsuspicious to me.
Comment 7 Dani Megert CLA 2011-08-16 02:45:30 EDT
The API expects an ICompilationUnit and the client can do what it wants with it and he definitely has not to expect that he gets some binary stuff. If internal code passes something to that API which then blows up, then it's an internal implementation problem. The inheritance in this case is implementation based which often causes trouble. Look for example at 'ISourceManipulation' which is implemented an ICU: it clearly states:

> Common protocol for Java elements that support source code manipulations such
> * as copy, move, rename, and delete.

So - if JDT Core passes binary elements to such an interface then this is just wrong.
Comment 8 Stephan Herrmann CLA 2011-08-17 09:51:30 EDT
(In reply to comment #7)
> The API expects an ICompilationUnit and the client can do what it wants with it
> and he definitely has not to expect that he gets some binary stuff.

Sorry, I was looking at various potential sources for inconsistencies.
It seems I missed that all your concerns were just about the contract
of WorkingCopyOwner.createBuffer(ICompilationUnit).

Investigating further I see that it was bug 110160 where Jerome
"Added API IClassFile#becomeWorkingCopy(IProblemRequestor, WorkingCopyOwner,
 IProgressMonitor) that returns an ICompilationUnit in working copy mode on
 the given class file."

As part of the patch the first real implementation was added to class
ClassFileWorkingCopy, incl. method openBuffer which calls 
  IBuffer buffer = this.owner.createBuffer(this);

Strictly speaking this line breaks API, but actually the whole notion
of a ClassFileWorkingCopy is problematic, no?

Question: have the split refactorings mentioned in bug 110160 ever been
implemented? If so, which tests relate to that feature?

I tried to fix this by avoiding the above call to createBuffer.
I succeeded to make all of AllJavaModelTests pass which one exception:
org.eclipse.jdt.core.tests.model.WorkingCopyTests.testOnClassFile()
which explicitly tests use of a custom working copy owner for creating
custom buffers for class files. This particular test is not related to
any bug, as it is older than the eclipse CVS. So, how relevant is that
test, anyone know any use cases diving into this pond?

According to how I understand Dani, that test case cannot PASS without
breaking an API contract, right?
Comment 9 Stephan Herrmann CLA 2011-08-17 09:55:26 EDT
Created attachment 201644 [details]
alternative fix

This is how we can avoid breaking the contract of createBuffer for
the price of breaking
org.eclipse.jdt.core.tests.model.WorkingCopyTests.testOnClassFile()
Comment 10 Dani Megert CLA 2011-08-18 04:12:06 EDT
> Strictly speaking this line breaks API, but actually the whole notion
> of a ClassFileWorkingCopy is problematic, no?
Yes, this is due to the implementation inheritance.

> Question: have the split refactorings mentioned in bug 110160 ever been
> implemented?
Yes, see bug 106207.

The patch looks good to me but I would use
IBuffer buffer = BufferManager.createBuffer(this);
Comment 11 Stephan Herrmann CLA 2011-09-04 17:50:36 EDT
Created attachment 202725 [details]
improved fix

OK, this patch adopts Dani's suggestion from comment 10 and disables
the bogus test WorkingCopyTests.testOnClassFile().

It'd be cool if anyone new the answer to part two of this:

> Question: have the split refactorings mentioned in bug 110160 ever been
> implemented? If so, which tests relate to that feature?

Anyway, all java model tests pass with this patch, even in the IDE :)

Jay, what do you think?
Comment 12 Stephan Herrmann CLA 2011-09-04 17:52:58 EDT
(In reply to comment #11)
> It'd be cool if anyone new the answer to part two of this:

Pls. read phonetically: "new" -> "knew" :)
Comment 13 Jay Arthanareeswaran CLA 2011-09-06 11:25:00 EDT
The tests are all passing. Perfect! Thanks Stephan and I am assigning the bug to you.
Comment 14 Stephan Herrmann CLA 2011-09-06 15:16:51 EDT
Released for 3.8 M2.
Comment 15 Olivier Thomann CLA 2011-09-13 12:33:39 EDT
I cannot even start the tests in that mode.
I am using a 4.2 I-build.
I am getting:
java.lang.IllegalArgumentException: Argument cannot be null
	at org.eclipse.swt.SWT.error(SWT.java:4264)
	at org.eclipse.swt.SWT.error(SWT.java:4198)
	at org.eclipse.swt.SWT.error(SWT.java:4169)
	at org.eclipse.swt.widgets.Widget.error(Widget.java:468)
	at org.eclipse.swt.widgets.Widget.checkParent(Widget.java:277)
	at org.eclipse.swt.widgets.Widget.<init>(Widget.java:149)
	at org.eclipse.swt.widgets.Control.<init>(Control.java:110)
	at org.eclipse.swt.widgets.Scrollable.<init>(Scrollable.java:75)
	at org.eclipse.swt.widgets.Composite.<init>(Composite.java:95)
	at org.eclipse.ui.splash.BasicSplashHandler.getBundleProgressMonitor(BasicSplashHandler.java:159)
	at org.eclipse.ui.internal.Workbench$3.run(Workbench.java:543)
	at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:332)
	at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:519)
	at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:149)
	at org.eclipse.ui.internal.ide.application.IDEApplication.start(IDEApplication.java:123)
	at org.eclipse.pde.internal.junit.runtime.NonUIThreadTestApplication.runApp(NonUIThreadTestApplication.java:54)
	at org.eclipse.pde.internal.junit.runtime.UITestApplication.runApp(UITestApplication.java:41)
	at org.eclipse.pde.internal.junit.runtime.NonUIThreadTestApplication.start(NonUIThreadTestApplication.java:48)
	at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:196)
	at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:110)
	at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:79)
	at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:344)
	at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:179)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
	at java.lang.reflect.Method.invoke(Method.java:597)
	at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:624)
	at org.eclipse.equinox.launcher.Main.basicRun(Main.java:579)
	at org.eclipse.equinox.launcher.Main.run(Main.java:1431)
	at org.eclipse.equinox.launcher.Main.main(Main.java:1407)
Comment 16 Olivier Thomann CLA 2011-09-13 12:47:05 EDT
In fact even UI tests don't run fine using that build. Investigating.
Comment 17 Olivier Thomann CLA 2011-09-13 13:06:13 EDT
This is related to bug 357521.
Once this is fixed, I should be able to verify this fix.
Comment 18 Olivier Thomann CLA 2011-09-13 15:21:45 EDT
With a fix from Bodgan, I could finally start UI tests and verify this fix. I am surprised nobody noticed that UI tests don't start in a 4.2 workspace.
Verified for 3.8M2.