Bug 564153 - Test failure for testBadIcon on Mac
Summary: Test failure for testBadIcon on Mac
Status: RESOLVED FIXED
Alias: None
Product: Platform
Classification: Eclipse Project
Component: UI (show other bugs)
Version: 4.17   Edit
Hardware: PC Mac OS X
: P3 normal (vote)
Target Milestone: 4.17 M1   Edit
Assignee: Alex Blewitt CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2020-06-10 01:50 EDT by Lars Vogel CLA
Modified: 2020-06-12 03:03 EDT (History)
2 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Lars Vogel CLA 2020-06-10 01:50:00 EDT
testBadIcon test fails on Mac. Se https://download.eclipse.org/eclipse/downloads/drops4/I20200609-1800/testresults/html/org.eclipse.ui.tests_ep417I-unit-mac64-java8_macosx.cocoa.x86_64_8.0.html


Stack trace below.

This may or may not be related to the recent icon work. 

N/A

java.lang.AssertionError
at org.junit.Assert.fail(Assert.java:87)
at org.junit.Assert.assertTrue(Assert.java:42)
at org.junit.Assert.assertTrue(Assert.java:53)
at org.eclipse.ui.tests.harness.util.ImageTests.assertEquals(ImageTests.java:37)
at org.eclipse.ui.tests.api.EditorIconTest.testBadIcon(EditorIconTest.java:97)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
at org.junit.rules.TestWatcher$1.evaluate(TestWatcher.java:61)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
at org.junit.runners.Suite.runChild(Suite.java:128)
at org.junit.runners.Suite.runChild(Suite.java:27)
at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
at org.junit.runners.Suite.runChild(Suite.java:128)
at org.junit.runners.Suite.runChild(Suite.java:27)
at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at org.junit.runner.JUnitCore.run(JUnitCore.java:115)
at org.junit.vintage.engine.execution.RunnerExecutor.execute(RunnerExecutor.java:43)
at java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:184)
at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)
at java.util.Iterator.forEachRemaining(Iterator.java:116)
at java.util.Spliterators$IteratorSpliterator.forEachRemaining(Spliterators.java:1801)
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
at java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:151)
at java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:174)
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:418)
at org.junit.vintage.engine.VintageTestEngine.executeAllChildren(VintageTestEngine.java:82)
at org.junit.vintage.engine.VintageTestEngine.execute(VintageTestEngine.java:73)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:248)
at org.junit.platform.launcher.core.DefaultLauncher.lambda$execute$5(DefaultLauncher.java:211)
at org.junit.platform.launcher.core.DefaultLauncher.withInterceptedStreams(DefaultLauncher.java:226)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:199)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:132)
at org.eclipse.test.EclipseTestRunner.runTests(EclipseTestRunner.java:226)
at org.eclipse.test.EclipseTestRunner.run(EclipseTestRunner.java:202)
at org.eclipse.test.UITestApplication.lambda$0(UITestApplication.java:103)
at org.eclipse.swt.widgets.RunnableLock.run(RunnableLock.java:40)
at org.eclipse.swt.widgets.Synchronizer.runAsyncMessages(Synchronizer.java:185)
at org.eclipse.swt.widgets.Display.runAsyncMessages(Display.java:4124)
at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:3791)
at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$5.run(PartRenderingEngine.java:1158)
at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:338)
at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.run(PartRenderingEngine.java:1047)
at org.eclipse.e4.ui.internal.workbench.E4Workbench.createAndRunUI(E4Workbench.java:155)
at org.eclipse.ui.internal.Workbench.lambda$3(Workbench.java:658)
at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:338)
at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:557)
at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:154)
at org.eclipse.ui.internal.ide.application.IDEApplication.start(IDEApplication.java:150)
at org.eclipse.test.UITestApplication.run(UITestApplication.java:48)
at org.eclipse.test.UITestApplication.start(UITestApplication.java:118)
at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:203)
at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:137)
at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:107)
at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:401)
at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:255)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:657)
at org.eclipse.equinox.launcher.Main.basicRun(Main.java:594)
at org.eclipse.equinox.launcher.Main.run(Main.java:1447)
at org.eclipse.equinox.launcher.Main.main(Main.java:1420)
at org.eclipse.core.launcher.Main.main(Main.java:44)
Comment 1 Lars Vogel CLA 2020-06-10 01:50:47 EDT
Alex, can you have a look?
Comment 2 Alex Blewitt CLA 2020-06-10 05:29:19 EDT
I'll see if I can have a look in the near future.
Comment 3 Alex Blewitt CLA 2020-06-10 11:25:21 EDT
This is a failure introduced because of the deferred JFace descriptor.
Comment 4 Alex Blewitt CLA 2020-06-10 13:17:33 EDT
For reasons that I do not yet understand, this test fails:

--- 8< ---
String path = "icons/full/obj16/file_obj.png";
Image a = ImageDescriptor.createFromURL(BundleUtility.find(PlatformUI.PLUGIN_ID, path)).createImage();
Image b = ImageDescriptor.createFromURLSupplier(true, () -> {
  return BundleUtility.find(PlatformUI.PLUGIN_ID, path);
}).createImage();
ImageTests.assertEquals(a, b);
--- 8< ---

The files are very slightly different to the tune of a few pixels. It's not clear if this is because the lookup of the bundle by ID is different and we're seeing slightly different versions of the image; there are a few file_obj.png with different values in the platform. Alternatively, it could be because of a file_obj vs file_obj@2.
Comment 5 Alex Blewitt CLA 2020-06-10 13:20:00 EDT
This, meanwhile, works:

Bundle bundle = FrameworkUtil.getBundle(WorkbenchImages.class);
String path = "icons/full/obj16/file_obj.png";
Image a = ImageDescriptor.createFromURL(BundleUtility.find(bundle, path)).createImage();
Image b = ImageDescriptor.createFromURLSupplier(true, () -> {
  return BundleUtility.find(bundle, path);
}).createImage();
ImageTests.assertEquals(a, b);

So I think it's an issue with the way that we are resolving the images when the URL is being evaluated.
Comment 6 Alex Blewitt CLA 2020-06-10 13:39:20 EDT
(In reply to Alex Blewitt from comment #5)
> This, meanwhile, works:
> 
> Bundle bundle = FrameworkUtil.getBundle(WorkbenchImages.class);

But only because this bundle doesn't contain the images, and so both are the missing image :(

I can re-reproduce the failure with 

--- 8< ---
Bundle bundle = Platform.getBundle("org.eclipse.ui");
String path = "icons/full/obj16/file_obj.png";
Image a = ImageDescriptor.createFromURL(BundleUtility.find(bundle, path)).createImage();
Image b = ImageDescriptor.createFromURLSupplier(true, () -> {
  return BundleUtility.find(bundle, path);
}).createImage();
ImageTests.assertEquals(a, b);
--- 8< ---
Comment 7 Lars Vogel CLA 2020-06-10 14:42:15 EDT
Not sure if that is related, but the equals method under Mac looks different from the one on Linux:

Mac:

@Override
public boolean equals (Object object) {
	if (object == this) return true;
	if (!(object instanceof Image)) return false;
	Image image = (Image)object;
	if (device != image.device || alphaInfo_100.transparentPixel != image.alphaInfo_100.transparentPixel) return false;
	if (imageDataProvider != null && image.imageDataProvider != null) {
		return styleFlag == image.styleFlag && imageDataProvider.equals (image.imageDataProvider);
	} else if (imageFileNameProvider != null && image.imageFileNameProvider != null) {
		return styleFlag == image.styleFlag && imageFileNameProvider.equals (image.imageFileNameProvider);
	} else {
		return handle == image.handle;
	}
}

Linux:

@Override
public boolean equals (Object object) {
	if (object == this) return true;
	if (!(object instanceof Image)) return false;
	Image image = (Image)object;
	if (device != image.device || transparentPixel != image.transparentPixel) return false;
	if (imageDataProvider != null && image.imageDataProvider != null) {
		return (styleFlag == image.styleFlag) && imageDataProvider.equals (image.imageDataProvider);
	} else if (imageFileNameProvider != null && image.imageFileNameProvider != null) {
		return (styleFlag == image.styleFlag) && imageFileNameProvider.equals (image.imageFileNameProvider);
	} else {
		return surface == image.surface;
	}
}

Different equals methods for images on different platforms feels like a bug to me.
Comment 8 Alex Blewitt CLA 2020-06-10 15:00:01 EDT
OK, so this also fails :(

--- 8< ---
Bundle bundle = Platform.getBundle("org.eclipse.ui");
String path = "icons/full/obj16/file_obj.png";
URL url = BundleUtility.find(bundle, path);
Image a = ImageDescriptor.createFromURL(url).createImage();
Image b = ImageDescriptor.createFromURLSupplier(true, () -> {
  return url;
}).createImage();
ImageTests.assertEquals(a, b);
--- 8< ---

At least it's reproducible ...
Comment 9 Alex Blewitt CLA 2020-06-10 15:20:41 EDT
The image data appears to be different in only 11 bytes, so it's very close. I wonder if there is some kind of failure caused by a high-resolution image being down-scaled somehow.
Comment 10 Alex Blewitt CLA 2020-06-10 17:10:54 EDT
I've not managed to find out why this occurs yet. We could resolve it by reverting c60b87a6bd828ca13543e38548919a7ed2e46446 which introduced the change to WorkbenchImages that causes this test to fail. However, it may not be causing problems in practice, and if it's only triggering this test failure on macOS we can leave it as is while we investigate this further.
Comment 11 Alex Blewitt CLA 2020-06-10 18:29:41 EDT
So this is interesting.

Image a = ImageDescriptor.createFromURL(url).createImage();
Image b = ImageDescriptor.createFromURLSupplier(true, () -> {
  return url;
}).createImage();
ImageLoader loader = new ImageLoader();
ImageData id_file = loader.load("/path/to/full/obj16/file_obj.png")[0];
ImageData id_stream = loader.load(url.openStream())[0];
Image i_file = new Image(Display.getCurrent(), id_file);
Image i_stream = new Image(Display.getCurrent(), id_stream);

Here, b/c/d all agree on the contents, and only a is different. I'd be tempted to argue that b is producing the right image, and that a is not.

Having debugged further into the code, it looks like what's happening:

createFromURL(url).createImage() delegates to new ImageData(url) which uses NSImage to load the image directly from the URL.

The createFromURLSupplier() uses the SWT image parsing to get the data object.

When you acquire the image from the ImageData then it will use the hi-res image, and then downscales that to generate the image data for the purposes of comparison.

We can fix this by adding an alternative API path for the DeferredImageDescriptor which will do the same thing as before.
Comment 12 Eclipse Genie CLA 2020-06-10 18:36:15 EDT
New Gerrit change created: https://git.eclipse.org/r/164673
Comment 13 Alex Blewitt CLA 2020-06-10 18:46:29 EDT
(In reply to Lars Vogel from comment #7)
> Not sure if that is related, but the equals method under Mac looks different
> from the one on Linux:
> ... 
> Different equals methods for images on different platforms feels like a bug
> to me.

Sounds like it. Want to file another bug?
Comment 14 Lars Vogel CLA 2020-06-11 11:02:45 EDT
Thanks, Alex.
Comment 16 Lars Vogel CLA 2020-06-11 11:07:08 EDT
(In reply to Alex Blewitt from comment #13)
> Sounds like it. Want to file another bug?

Bug 564211
Comment 18 Lars Vogel CLA 2020-06-12 03:03:35 EDT
Sorry, todays build failed so I was looking at old data.