# HG changeset patch
# User Alexander Nyßen <alexander@nyssen.org>
# Date 1469687845 -7200
#      Thu Jul 28 08:37:25 2016 +0200
# Node ID 8bf9a66b677be4850e699e91cd89c8b92f32618e
# Parent  286fab2d69580b1985d9af6aa699ce5215c3966c
8160325: [SWT] Provide public API to access FXCanvas for embedded scene

- Provided static FXCanvas#getFXCanvas(Scene) method to obtain the FXCanvas instance embedding the given Scene instance.
- Added EmbeddedWindow.getHost() so the HostInterface can be retrieved.
- Added FXCanvasTest with a test method to test correct behavior of FXCanvas#getFXCanvas(Scene).
- Introduced SwtTest JUnit MethodRule to have more concise tests and ensure it is also used by SWTCursorsTest.
- Ensured SWT tests are executed using GTK2 on Linux.

diff --git a/build.gradle b/build.gradle
--- a/build.gradle
+++ b/build.gradle
@@ -1866,7 +1866,7 @@
                 logger.info("SWT tests are disabled on MAC, because Gradle test runner does not handle -XstartOnFirstThread properly (https://issues.gradle.org/browse/GRADLE-3290).")
             }
             if (IS_LINUX) {
-                logger.info("SWT tests may cause 'Gtk-CRITICAL **: IA__gtk_main_quit: assertion 'main_loops != NULL' failed' LINUX (https://bugs.eclipse.org/bugs/show_bug.cgi?id=435066). This happens on shutdown, the tests are not affected.")
+                jvmArgs "-DSWT_GTK3=0"
             }
         }
     }
diff --git a/modules/graphics/src/main/java/com/sun/javafx/stage/EmbeddedWindow.java b/modules/graphics/src/main/java/com/sun/javafx/stage/EmbeddedWindow.java
--- a/modules/graphics/src/main/java/com/sun/javafx/stage/EmbeddedWindow.java
+++ b/modules/graphics/src/main/java/com/sun/javafx/stage/EmbeddedWindow.java
@@ -53,6 +53,10 @@
         EmbeddedWindowHelper.initHelper(this);
     }
 
+    public HostInterface getHost(){
+        return host;
+    }
+
     /**
      * Specify the scene to be used on this stage.
      */
diff --git a/modules/swt/src/main/java/javafx/embed/swt/FXCanvas.java b/modules/swt/src/main/java/javafx/embed/swt/FXCanvas.java
--- a/modules/swt/src/main/java/javafx/embed/swt/FXCanvas.java
+++ b/modules/swt/src/main/java/javafx/embed/swt/FXCanvas.java
@@ -53,6 +53,7 @@
 import javafx.beans.NamedArg;
 import javafx.scene.Scene;
 import javafx.scene.input.TransferMode;
+import javafx.stage.Window;
 import javafx.util.FXPermission;
 
 import org.eclipse.swt.SWT;
@@ -100,7 +101,7 @@
  * {@code FXCanvas} is a component to embed JavaFX content into
  * SWT applications. The content to be displayed is specified
  * with the {@link #setScene} method that accepts an instance of
- * JavaFX {@code Scene}. After the scene is assigned, it gets
+ * JavaFX {@code Scene}. After the scene is attached, it gets
  * repainted automatically. All the input and focus events are
  * forwarded to the scene transparently to the developer.
  * <p>
@@ -131,7 +132,6 @@
  *    }
  * </pre>
  *
- *
  * @since JavaFX 2.0
  */
 public class FXCanvas extends Canvas {
@@ -164,7 +164,8 @@
                 break;
             }
             control = control.getParent();
-        };
+        }
+        ;
     };
 
     private double getScaleFactor() {
@@ -186,31 +187,31 @@
 
     private DropTarget dropTarget;
 
-    static Transfer [] StandardTransfers = new Transfer [] {
-        TextTransfer.getInstance(),
-        RTFTransfer.getInstance(),
-        HTMLTransfer.getInstance(),
-        URLTransfer.getInstance(),
-        ImageTransfer.getInstance(),
-        FileTransfer.getInstance(),
+    static Transfer[] StandardTransfers = new Transfer[]{
+            TextTransfer.getInstance(),
+            RTFTransfer.getInstance(),
+            HTMLTransfer.getInstance(),
+            URLTransfer.getInstance(),
+            ImageTransfer.getInstance(),
+            FileTransfer.getInstance(),
     };
-    static Transfer [] CustomTransfers = new Transfer [0];
+    static Transfer[] CustomTransfers = new Transfer[0];
 
-    static Transfer [] getAllTransfers () {
-        Transfer [] transfers = new Transfer[StandardTransfers.length + CustomTransfers.length];
+    static Transfer[] getAllTransfers() {
+        Transfer[] transfers = new Transfer[StandardTransfers.length + CustomTransfers.length];
         System.arraycopy(StandardTransfers, 0, transfers, 0, StandardTransfers.length);
         System.arraycopy(CustomTransfers, 0, transfers, StandardTransfers.length, CustomTransfers.length);
         return transfers;
     }
 
     static Transfer getCustomTransfer(String mime) {
-        for (int i=0; i<CustomTransfers.length; i++) {
-            if (((CustomTransfer)CustomTransfers[i]).getMime().equals(mime)) {
+        for (int i = 0; i < CustomTransfers.length; i++) {
+            if (((CustomTransfer) CustomTransfers[i]).getMime().equals(mime)) {
                 return CustomTransfers[i];
             }
         }
-        Transfer transfer = new CustomTransfer (mime, mime);
-        Transfer [] newCustom = new Transfer [CustomTransfers.length + 1];
+        Transfer transfer = new CustomTransfer(mime, mime);
+        Transfer[] newCustom = new Transfer[CustomTransfers.length + 1];
         System.arraycopy(CustomTransfers, 0, newCustom, 0, CustomTransfers.length);
         newCustom[CustomTransfers.length] = transfer;
         CustomTransfers = newCustom;
@@ -258,6 +259,36 @@
         display.addFilter(SWT.Move, moveFilter);
     }
 
+    /**
+     * Retrieves the {@code FXCanvas} embedding the given {@code Scene},
+     * i.e. the {@code FXCanvas}, to which the given {@code Scene} was
+     * attached using {@link #setScene}.
+     *
+     * @param scene The {@code Scene} whose embedding {@code FXCanvas}
+     *              instance is to be retrieved
+     * @return the {@code FXCanvas} to which the given {@code Scene} is
+     * attached, or null of the given {@code Scene} is not attached to an
+     * {@code FXCanvas}.
+     */
+    public static FXCanvas getFXCanvas(Scene scene) {
+        Window window = scene.getWindow();
+        if (window != null && window instanceof EmbeddedWindow) {
+            HostInterface hostInterface = ((EmbeddedWindow) window).getHost();
+            if (hostInterface instanceof HostContainer) {
+                // Obtain FXCanvas as the enclosing instance of the FXCanvas$HostContainer
+                // that is host of the embedded Scene's EmbeddedWindow.
+                try {
+                    Field this$0 = HostContainer.class.getDeclaredField("this$0");
+                    this$0.setAccessible(true);
+                    return (FXCanvas) this$0.get(hostInterface);
+                } catch (Exception e) {
+                    return null;
+                }
+            }
+        }
+        return null;
+    }
+
     private static void initFx() {
         // NOTE: no internal "com.sun.*" packages can be accessed until after
         // the JavaFX platform is initialized. The list of needed internal
@@ -303,7 +334,7 @@
     }
 
     private void setApplicationName(String name) {
-        Platform.runLater(()-> Application.GetApplication().setName(name));
+        Platform.runLater(() -> Application.GetApplication().setName(name));
     }
 
     static ArrayList<DropTarget> targets = new ArrayList<>();
@@ -342,11 +373,11 @@
     /**
      * {@inheritDoc}
      */
-    public Point computeSize (int wHint, int hHint, boolean changed) {
+    public Point computeSize(int wHint, int hHint, boolean changed) {
         checkWidget();
         if (wHint == -1 && hHint == -1) {
             if (pPreferredWidth != -1 && pPreferredHeight != -1) {
-                return new Point (pPreferredWidth, pPreferredHeight);
+                return new Point(pPreferredWidth, pPreferredHeight);
             }
         }
         return super.computeSize(wHint, hHint, changed);
@@ -369,7 +400,6 @@
      * event dispatch thread).
      *
      * @param newScene a scene to display in this {@code FXCanvas}
-     *
      * @see javafx.application.Platform#isFxApplicationThread()
      */
     public void setScene(final Scene newScene) {
@@ -409,12 +439,14 @@
             public void mouseDoubleClick(MouseEvent me) {
                 // Clicks and double-clicks are handled in FX
             }
+
             @Override
             public void mouseDown(MouseEvent me) {
                 // FX only supports 3 buttons so don't send the event for other buttons
                 if (me.button > 3) return;
                 FXCanvas.this.sendMouseEventToFX(me, AbstractEvents.MOUSEEVENT_PRESSED);
             }
+
             @Override
             public void mouseUp(MouseEvent me) {
                 // FX only supports 3 buttons so don't send the event for other buttons
@@ -445,10 +477,12 @@
             public void mouseEnter(MouseEvent me) {
                 FXCanvas.this.sendMouseEventToFX(me, AbstractEvents.MOUSEEVENT_ENTERED);
             }
+
             @Override
             public void mouseExit(MouseEvent me) {
                 FXCanvas.this.sendMouseEventToFX(me, AbstractEvents.MOUSEEVENT_EXITED);
             }
+
             @Override
             public void mouseHover(MouseEvent me) {
                 // No mouse hovering in FX
@@ -460,6 +494,7 @@
             public void controlMoved(ControlEvent ce) {
                 FXCanvas.this.sendMoveEventToFX();
             }
+
             @Override
             public void controlResized(ControlEvent ce) {
                 FXCanvas.this.sendResizeEventToFX();
@@ -471,6 +506,7 @@
             public void focusGained(FocusEvent fe) {
                 FXCanvas.this.sendFocusEventToFX(fe, true);
             }
+
             @Override
             public void focusLost(FocusEvent fe) {
                 FXCanvas.this.sendFocusEventToFX(fe, false);
@@ -483,6 +519,7 @@
                 FXCanvas.this.sendKeyEventToFX(e, SWT.KeyDown);
 
             }
+
             @Override
             public void keyReleased(KeyEvent e) {
                 FXCanvas.this.sendKeyEventToFX(e, SWT.KeyUp);
@@ -516,7 +553,8 @@
 
     double lastScaleFactor = 1.0;
     int lastWidth, lastHeight;
-    IntBuffer lastPixelsBuf =  null;
+    IntBuffer lastPixelsBuf = null;
+
     private void paintControl(PaintEvent pe) {
         if ((scenePeer == null) || (pixelsBuf == null)) {
             return;
@@ -526,7 +564,7 @@
         if (lastScaleFactor != scaleFactor) {
             resizePixelBuffer(scaleFactor);
             lastScaleFactor = scaleFactor;
-            scenePeer.setPixelScaleFactors((float)scaleFactor, (float)scaleFactor);
+            scenePeer.setPixelScaleFactors((float) scaleFactor, (float) scaleFactor);
         }
 
         // if we can't get the pixels, draw the bits that were there before
@@ -542,8 +580,8 @@
             height = lastHeight;
             buffer = lastPixelsBuf;
         }
-        width = (int)Math.round(width * scaleFactor);
-        height = (int)Math.round(height * scaleFactor);
+        width = (int) Math.round(width * scaleFactor);
+        height = (int) Math.round(height * scaleFactor);
 
         // Consider optimizing this
         ImageData imageData = null;
@@ -557,12 +595,13 @@
                 for (int x = 0; x < width; x++) {
                     int p = srcData[sp++];
                     dstData[dp++] = (byte) (p & 0xFF); //dst:blue
-                    dstData[dp++] = (byte)((p >> 8) & 0xFF); //dst:green
-                    dstData[dp++] = (byte)((p >> 16) & 0xFF); //dst:green
-                    dstData[dp++] = (byte)0x00; //alpha
+                    dstData[dp++] = (byte) ((p >> 8) & 0xFF); //dst:green
+                    dstData[dp++] = (byte) ((p >> 16) & 0xFF); //dst:green
+                    dstData[dp++] = (byte) 0x00; //alpha
                 }
             }
-            /*ImageData*/ imageData = new ImageData(width, height, 32, palette, 4, dstData);
+            /*ImageData*/
+            imageData = new ImageData(width, height, 32, palette, 4, dstData);
         } else {
             if (width * height > buffer.array().length) {
                 // We shouldn't be here...
@@ -570,8 +609,9 @@
                 return;
             }
             PaletteData palette = new PaletteData(0x00ff0000, 0x0000ff00, 0x000000ff);
-            /*ImageData*/  imageData = new ImageData(width, height, 32, palette);
-            imageData.setPixels(0, 0,width * height, buffer.array(), 0);
+            /*ImageData*/
+            imageData = new ImageData(width, height, 32, palette);
+            imageData.setPixels(0, 0, width * height, buffer.array(), 0);
         }
 
         Image image = new Image(Display.getDefault(), imageData);
@@ -672,7 +712,7 @@
                 keyCode, new char[0],
                 SWTEvents.keyModifiersToEmbedKeyModifiers(stateMask));
         if (e.character != '\0' && type == SWT.KeyDown) {
-            char[] chars = new char[] { e.character };
+            char[] chars = new char[]{e.character};
             scenePeer.keyEvent(
                     AbstractEvents.KEYEVENT_TYPED,
                     e.keyCode, chars,
@@ -712,8 +752,8 @@
         if ((pWidth <= 0) || (pHeight <= 0)) {
             pixelsBuf = null;
         } else {
-            pixelsBuf = IntBuffer.allocate((int)Math.round(pWidth * newScaleFactor) *
-                                           (int)Math.round(pHeight * newScaleFactor));
+            pixelsBuf = IntBuffer.allocate((int) Math.round(pWidth * newScaleFactor) *
+                    (int) Math.round(pHeight * newScaleFactor));
             // The bg color may show through on resize. See RT-34380.
             RGB rgb = getBackground().getRGB();
             Arrays.fill(pixelsBuf.array(), rgb.red << 16 | rgb.green << 8 | rgb.blue);
@@ -725,8 +765,8 @@
             return;
         }
         int focusCause = (focused ?
-                          AbstractEvents.FOCUSEVENT_ACTIVATED :
-                          AbstractEvents.FOCUSEVENT_DEACTIVATED);
+                AbstractEvents.FOCUSEVENT_ACTIVATED :
+                AbstractEvents.FOCUSEVENT_DEACTIVATED);
         stagePeer.setFocused(focused, focusCause);
     }
 
@@ -758,7 +798,7 @@
                 case DND.DROP_LINK:
                     return TransferMode.LINK;
                 default:
-                   return null;
+                    return null;
             }
         }
 
@@ -825,7 +865,7 @@
 
         // Consider using dragAction
         private DragSource createDragSource(final EmbeddedSceneDSInterface fxDragSource, TransferMode dragAction) {
-            Transfer [] transfers = getTransferTypes(fxDragSource.getMimeTypes());
+            Transfer[] transfers = getTransferTypes(fxDragSource.getMimeTypes());
             if (transfers.length == 0) return null;
             int dragOperation = getDragActions(fxDragSource.getSupportedActions());
             final DragSource dragSource = new DragSource(FXCanvas.this, dragOperation);
@@ -835,16 +875,17 @@
                     dragSource.dispose();
                     fxDragSource.dragDropEnd(getTransferMode(event.detail));
                 }
+
                 public void dragSetData(org.eclipse.swt.dnd.DragSourceEvent event) {
-                    Transfer [] transfers = dragSource.getTransfer();
-                    for (int i=0; i<transfers.length; i++) {
+                    Transfer[] transfers = dragSource.getTransfer();
+                    for (int i = 0; i < transfers.length; i++) {
                         if (transfers[i].isSupportedType(event.dataType)) {
                             String mime = getMime(transfers[i]);
                             if (mime != null) {
                                 event.doit = true;
                                 event.data = fxDragSource.getData(mime);
                                 if (event.data instanceof Pixels) {
-                                    event.data = createImageData((Pixels)event.data);
+                                    event.data = createImageData((Pixels) event.data);
                                 }
                                 return;
                             }
@@ -852,6 +893,7 @@
                         event.doit = false;
                     }
                 }
+
                 public void dragStart(org.eclipse.swt.dnd.DragSourceEvent event) {
                 }
             });
@@ -861,9 +903,12 @@
         int getDragAction(TransferMode tm) {
             if (tm == null) return DND.DROP_NONE;
             switch (tm) {
-                case COPY: return DND.DROP_COPY;
-                case MOVE: return DND.DROP_MOVE;
-                case LINK: return DND.DROP_LINK;
+                case COPY:
+                    return DND.DROP_COPY;
+                case MOVE:
+                    return DND.DROP_MOVE;
+                case LINK:
+                    return DND.DROP_LINK;
                 default:
                     throw new IllegalArgumentException("Invalid transfer mode");
             }
@@ -889,15 +934,15 @@
             return getCustomTransfer(mime);
         }
 
-        Transfer [] getTransferTypes(String [] mimeTypes) {
-            int count= 0;
-            Transfer [] transfers = new Transfer [mimeTypes.length];
-            for (int i=0; i<mimeTypes.length; i++) {
+        Transfer[] getTransferTypes(String[] mimeTypes) {
+            int count = 0;
+            Transfer[] transfers = new Transfer[mimeTypes.length];
+            for (int i = 0; i < mimeTypes.length; i++) {
                 Transfer transfer = getTransferType(mimeTypes[i]);
-                if (transfer != null) transfers [count++] = transfer;
+                if (transfer != null) transfers[count++] = transfer;
             }
             if (count != mimeTypes.length) {
-                Transfer [] newTransfers = new Transfer[count];
+                Transfer[] newTransfers = new Transfer[count];
                 System.arraycopy(transfers, 0, newTransfers, 0, count);
                 transfers = newTransfers;
             }
@@ -906,25 +951,26 @@
 
         String getMime(Transfer transfer) {
             if (transfer.equals(TextTransfer.getInstance())) return "text/plain";
-            if (transfer.equals(RTFTransfer.getInstance())) return "text/rtf"; ;
-            if (transfer.equals( HTMLTransfer.getInstance())) return "text/html";
+            if (transfer.equals(RTFTransfer.getInstance())) return "text/rtf";
+            ;
+            if (transfer.equals(HTMLTransfer.getInstance())) return "text/html";
             if (transfer.equals(URLTransfer.getInstance())) return "text/uri-list";
-            if (transfer.equals( ImageTransfer.getInstance())) return "application/x-java-rawimage";
+            if (transfer.equals(ImageTransfer.getInstance())) return "application/x-java-rawimage";
             if (transfer.equals(FileTransfer.getInstance())) return "application/x-java-file-list";
-            if (transfer instanceof CustomTransfer) return ((CustomTransfer)transfer).getMime();
+            if (transfer instanceof CustomTransfer) return ((CustomTransfer) transfer).getMime();
             return null;
         }
 
-        String [] getMimes(Transfer [] transfers, TransferData data) {
-            int count= 0;
-            String [] result = new String [transfers.length];
-            for (int i=0; i<transfers.length; i++) {
+        String[] getMimes(Transfer[] transfers, TransferData data) {
+            int count = 0;
+            String[] result = new String[transfers.length];
+            for (int i = 0; i < transfers.length; i++) {
                 if (transfers[i].isSupportedType(data)) {
-                    result [count++] = getMime (transfers [i]);
+                    result[count++] = getMime(transfers[i]);
                 }
             }
             if (count != result.length) {
-                String [] newResult = new String[count];
+                String[] newResult = new String[count];
                 System.arraycopy(result, 0, newResult, 0, count);
                 result = newResult;
             }
@@ -950,34 +996,40 @@
                     public Set<TransferMode> getSupportedActions() {
                         return getTransferModes(operations);
                     }
+
                     public Object getData(String mimeType) {
                         // NOTE: get the data for the requested mime type, not the default data
                         return data;
                     }
+
                     public String[] getMimeTypes() {
-                        if (currentTransferData == null) return new String [0];
+                        if (currentTransferData == null) return new String[0];
                         return getMimes(getAllTransfers(), currentTransferData);
                     }
+
                     public boolean isMimeTypeAvailable(String mimeType) {
-                        String [] mimes = getMimeTypes();
-                        for (int i=0; i<mimes.length; i++) {
+                        String[] mimes = getMimeTypes();
+                        for (int i = 0; i < mimes.length; i++) {
                             if (mimes[i].equals(mimeType)) return true;
                         }
                         return false;
                     }
+
                     public void dragDropEnd(TransferMode performedAction) {
                         data = null;
                         //transferData = null;
                         currentTransferData = null;
                     }
                 };
+
                 public void dragEnter(DropTargetEvent event) {
                     ignoreLeave = false;
                     dropTarget.setTransfer(getAllTransfers());
                     detail = event.detail;
                     operations = event.operations;
-                    dragOver (event, true, detail);
+                    dragOver(event, true, detail);
                 }
+
                 public void dragLeave(DropTargetEvent event) {
                     detail = operations = DND.DROP_NONE;
                     data = null;
@@ -988,15 +1040,18 @@
                         fxDropTarget.handleDragLeave();
                     });
                 }
+
                 public void dragOperationChanged(DropTargetEvent event) {
                     detail = event.detail;
                     operations = event.operations;
                     dragOver(event, false, detail);
                 }
+
                 public void dragOver(DropTargetEvent event) {
                     operations = event.operations;
-                    dragOver (event, false, detail);
+                    dragOver(event, false, detail);
                 }
+
                 public void dragOver(DropTargetEvent event, boolean enter, int detail) {
                     //transferData = event.dataTypes;
                     currentTransferData = event.currentDataType;
@@ -1010,6 +1065,7 @@
                     }
                     event.detail = getDragAction(acceptedMode);
                 }
+
                 public void drop(DropTargetEvent event) {
                     detail = event.detail;
                     operations = event.operations;
@@ -1024,6 +1080,7 @@
                     //transferData = null;
                     currentTransferData = null;
                 }
+
                 public void dropAccept(DropTargetEvent event) {
                     ignoreLeave = true;
                 }
@@ -1043,7 +1100,7 @@
             double scaleFactor = getScaleFactor();
             resizePixelBuffer(scaleFactor);
             lastScaleFactor = scaleFactor;
-            scenePeer.setPixelScaleFactors((float)scaleFactor, (float)scaleFactor);
+            scenePeer.setPixelScaleFactors((float) scaleFactor, (float) scaleFactor);
             scenePeer.setDragStartListener((fxDragSource, dragAction) -> {
                 Platform.runLater(() -> {
                     DragSource dragSource = createDragSource(fxDragSource, dragAction);
@@ -1077,6 +1134,7 @@
 
         Object lock = new Object();
         boolean queued = false;
+
         public void repaint() {
             synchronized (lock) {
                 if (queued) return;
diff --git a/modules/swt/src/test/java/test/javafx/embed/swt/SWTCursorsTest.java b/modules/swt/src/test/java/test/javafx/embed/swt/FXCanvasTest.java
copy from modules/swt/src/test/java/test/javafx/embed/swt/SWTCursorsTest.java
copy to modules/swt/src/test/java/test/javafx/embed/swt/FXCanvasTest.java
--- a/modules/swt/src/test/java/test/javafx/embed/swt/SWTCursorsTest.java
+++ b/modules/swt/src/test/java/test/javafx/embed/swt/FXCanvasTest.java
@@ -27,89 +27,34 @@
 
 import javafx.embed.swt.FXCanvas;
 import javafx.scene.Group;
-import javafx.scene.ImageCursor;
 import javafx.scene.Scene;
-import javafx.scene.image.Image;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.widgets.Display;
 import org.eclipse.swt.widgets.Shell;
+import org.junit.Rule;
 import org.junit.Test;
 
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.atomic.AtomicReference;
+import static org.junit.Assert.assertSame;
 
-import static org.junit.Assert.assertNotNull;
+public class FXCanvasTest {
 
-public class SWTCursorsTest {
+    @Rule
+    public SwtRule ctx = new SwtRule();
 
-    @Test(timeout=10000)
-    public void testImageCursor() throws Throwable {
-        // create display on first thread (required when using SWT on Mac OS X)
-        final Display display = new Display();
-        final Shell shell = new Shell(display);
+    @Test(timeout = 10000)
+    public void getFXCanvas() throws Throwable {
+        final Shell shell = new Shell(Display.getCurrent());
         final FXCanvas canvas = new FXCanvas(shell, SWT.NONE);
         shell.open();
 
-        // keep track of exceptions thrown in UI thread
-        final AtomicReference<Throwable> throwableRef = new AtomicReference<>();
+        // create and hook scene
+        Scene scene = new Scene(new Group());
+        canvas.setScene(scene);
 
-        final CountDownLatch latch = new CountDownLatch(2);
-        display.asyncExec(() -> {
-            try {
-                // create and hook scene
-                Scene scene = new Scene(new Group());
-                canvas.setScene(scene);
+        // check FXCanvas is properly retrieved
+        assertSame(canvas, FXCanvas.getFXCanvas(canvas.getScene()));
 
-                // set image cursor to scene
-                Image cursorImage = new Image("test/javafx/embed/swt/cursor.png");
-                scene.setCursor(new ImageCursor(cursorImage));
-
-            } catch (Throwable throwable) {
-                throwableRef.set(throwable);
-            } finally {
-                latch.countDown();
-            }
-        });
-
-        while (latch.getCount() > 1) {
-            // run SWT event loop
-            if (!display.readAndDispatch()) {
-                display.sleep();
-            }
-        }
-
-        rethrow(throwableRef);
-
-        // ensure at least one pulse has passed before asserting the cursor is set,
-        // as the scene property synchronization is triggered by the pulse listener
-        display.asyncExec(() -> {
-            try {
-                assertNotNull(canvas.getCursor());
-            } catch (Throwable throwable) {
-                throwableRef.set(throwable);
-            } finally {
-                latch.countDown();
-            }
-        });
-
-        while (latch.getCount() > 0) {
-            // run SWT event loop (again)
-            if (!display.readAndDispatch()) {
-                display.sleep();
-            }
-        }
-
-        shell.close();
-        shell.dispose();
-        display.dispose();
-
-        rethrow(throwableRef);
-    }
-
-    private void rethrow(AtomicReference<Throwable> throwableRef) throws Throwable {
-        Throwable thrown = throwableRef.get();
-        if (thrown != null) {
-            throw thrown;
-        }
+        // FIXME: We cannot close the shell here because of https://bugs.eclipse.org/bugs/show_bug.cgi?id=435066.
+        // shell.close();
     }
 }
diff --git a/modules/swt/src/test/java/test/javafx/embed/swt/SWTCursorsTest.java b/modules/swt/src/test/java/test/javafx/embed/swt/SWTCursorsTest.java
--- a/modules/swt/src/test/java/test/javafx/embed/swt/SWTCursorsTest.java
+++ b/modules/swt/src/test/java/test/javafx/embed/swt/SWTCursorsTest.java
@@ -33,83 +33,35 @@
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.widgets.Display;
 import org.eclipse.swt.widgets.Shell;
+import org.junit.Rule;
 import org.junit.Test;
 
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.atomic.AtomicReference;
-
 import static org.junit.Assert.assertNotNull;
 
 public class SWTCursorsTest {
 
-    @Test(timeout=10000)
+    @Rule
+    public SwtRule ctx = new SwtRule();
+
+    @Test(timeout = 10000)
     public void testImageCursor() throws Throwable {
-        // create display on first thread (required when using SWT on Mac OS X)
-        final Display display = new Display();
-        final Shell shell = new Shell(display);
+        final Shell shell = new Shell(Display.getCurrent());
         final FXCanvas canvas = new FXCanvas(shell, SWT.NONE);
         shell.open();
 
-        // keep track of exceptions thrown in UI thread
-        final AtomicReference<Throwable> throwableRef = new AtomicReference<>();
+        // create and hook scene
+        Scene scene = new Scene(new Group());
+        canvas.setScene(scene);
 
-        final CountDownLatch latch = new CountDownLatch(2);
-        display.asyncExec(() -> {
-            try {
-                // create and hook scene
-                Scene scene = new Scene(new Group());
-                canvas.setScene(scene);
+        // set image cursor to scene
+        Image cursorImage = new Image("test/javafx/embed/swt/cursor.png");
+        scene.setCursor(new ImageCursor(cursorImage));
 
-                // set image cursor to scene
-                Image cursorImage = new Image("test/javafx/embed/swt/cursor.png");
-                scene.setCursor(new ImageCursor(cursorImage));
+        Display.getCurrent().asyncExec(() -> {
+            assertNotNull(canvas.getCursor());
 
-            } catch (Throwable throwable) {
-                throwableRef.set(throwable);
-            } finally {
-                latch.countDown();
-            }
+            // FIXME: We cannot close the shell here because of https://bugs.eclipse.org/bugs/show_bug.cgi?id=435066.
+            //shell.close();
         });
-
-        while (latch.getCount() > 1) {
-            // run SWT event loop
-            if (!display.readAndDispatch()) {
-                display.sleep();
-            }
-        }
-
-        rethrow(throwableRef);
-
-        // ensure at least one pulse has passed before asserting the cursor is set,
-        // as the scene property synchronization is triggered by the pulse listener
-        display.asyncExec(() -> {
-            try {
-                assertNotNull(canvas.getCursor());
-            } catch (Throwable throwable) {
-                throwableRef.set(throwable);
-            } finally {
-                latch.countDown();
-            }
-        });
-
-        while (latch.getCount() > 0) {
-            // run SWT event loop (again)
-            if (!display.readAndDispatch()) {
-                display.sleep();
-            }
-        }
-
-        shell.close();
-        shell.dispose();
-        display.dispose();
-
-        rethrow(throwableRef);
-    }
-
-    private void rethrow(AtomicReference<Throwable> throwableRef) throws Throwable {
-        Throwable thrown = throwableRef.get();
-        if (thrown != null) {
-            throw thrown;
-        }
     }
 }
diff --git a/modules/swt/src/test/java/test/javafx/embed/swt/SwtRule.java b/modules/swt/src/test/java/test/javafx/embed/swt/SwtRule.java
new file mode 100644
--- /dev/null
+++ b/modules/swt/src/test/java/test/javafx/embed/swt/SwtRule.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package test.javafx.embed.swt;
+
+import org.eclipse.swt.widgets.Display;
+import org.junit.rules.MethodRule;
+import org.junit.runners.model.FrameworkMethod;
+import org.junit.runners.model.Statement;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * A {@code MethodRule} to execute test methods synchronously on the SWT UI thread. The execution will wait for any
+ * asynchronous runnables scheduled by the test method on the SWT UI thread during its execution.
+ */
+public class SwtRule implements MethodRule {
+
+    private void rethrow(final AtomicReference<Throwable> throwableRef) throws Throwable {
+        Throwable thrown = throwableRef.get();
+        if (thrown != null) {
+            throw thrown;
+        }
+    }
+
+    @Override
+    public Statement apply(final Statement base, final FrameworkMethod testMethod, final Object target) {
+        return new Statement() {
+
+            public void evaluate() throws Throwable {
+                Display display = Display.getDefault();
+
+                // keep track of exceptions thrown in UI thread
+                final AtomicReference<Throwable> throwableRef = new AtomicReference<>();
+
+                final CountDownLatch latch = new CountDownLatch(1);
+                display.asyncExec(() -> {
+                    try {
+                        // ensure test method is synchronously executed (without spawning a new thread)
+                        testMethod.invokeExplosively(target);
+                    } catch (Throwable throwable) {
+                        throwableRef.set(throwable);
+                    } finally {
+                        display.asyncExec(() -> {
+                            // wait for any runnables scheduled (asynchronously)
+                            // by test method on the UI thread
+                            latch.countDown();
+                        });
+                    }
+                });
+
+                while (latch.getCount() > 0) {
+                    // run SWT event loop
+                    if (!display.readAndDispatch()) {
+                        display.sleep();
+                    }
+                }
+
+                rethrow(throwableRef);
+            }
+        };
+    }
+}
