--- old/tests/system/src/test/java/test/memoryleak/JSCallbackMemoryTest.java 2017-01-16 22:01:17.090117700 +0530 +++ new/tests/system/src/test/java/test/memoryleak/JSCallbackMemoryTest.java 2017-01-16 22:01:15.704101500 +0530 @@ -87,6 +87,8 @@ private final Object[] objectArray = { new Object(), new Object(), new Object(), new Object() }; + private AssertionError encounteredException = null; + public final class MyObject { // called from JavaScript @@ -220,10 +222,37 @@ }); } + private boolean isAllStagesNull() { + Set> collected = new HashSet<>(); + + for (WeakReference ref : refs) { + if (ref.get() != null) { + return false; + } + collected.add(ref); + } + + refs.removeAll(collected); + + return true; + } + + private boolean isAllCallbackStatusTrue() { + + for (int i = 0; i < NUM_STAGES; i++) { + if (callbackStatus[i] == false) { + return false; + } + } + + return true; + } + // ========================== TEST CASES ========================== @Test public void testJsCallbackLeak() throws Exception { + final CountDownLatch latch = new CountDownLatch(1); Util.runAndWait(() -> { @@ -244,9 +273,15 @@ webview.getEngine().getLoadWorker().stateProperty().addListener((ov, o, n) -> { if (n == Worker.State.SUCCEEDED) { - final JSObject window = (JSObject) webview.getEngine().executeScript("window"); - assertNotNull(window); - window.setMember("callback1", stage); + try { + final JSObject window = (JSObject) webview.getEngine().executeScript("window"); + assertNotNull(window); + window.setMember("callback1", stage); + } catch(AssertionError ex) { + encounteredException = ex; + } finally { + latch.countDown(); + } } }); @@ -256,7 +291,15 @@ } }); - Util.sleep(SLEEP_TIME); + if (encounteredException != null) { + throw encounteredException; + } + + try { + latch.await(); + } catch (InterruptedException ex) { + throw new AssertionError(ex); + } Util.runAndWait(() -> { @@ -267,33 +310,22 @@ }); - for (int j = 0; j < 2; j++) { + for (int j = 0; j < 5; j++) { System.gc(); - Util.sleep(SLEEP_TIME); - Set> collected = new HashSet<>(); + System.runFinalization(); - for (WeakReference ref : refs) { - if (ref.get() == null) { - collected.add(ref); - } + if (isAllStagesNull()) { + break; } - refs.removeAll(collected); - if (j == 0) { - // First time GC -> Collected will be equal to NUM_STAGES and refs size (remaining) will be 0 - assertEquals(NUM_STAGES, collected.size()); - assertEquals(0, refs.size()); - } - else { - // Second time GC -> Collected will be 0 and refs size (remaining) will be 0 - assertEquals(0, collected.size()); - assertEquals(0, refs.size()); - } + Util.sleep(SLEEP_TIME); } + assertEquals(0, refs.size()); } @Test public void testJsCallbackFunction() throws Exception { + final CountDownLatch latch = new CountDownLatch(1); Util.runAndWait(() -> { @@ -314,10 +346,16 @@ webview.getEngine().getLoadWorker().stateProperty().addListener((ov, o, n) -> { if (n == Worker.State.SUCCEEDED) { - final JSObject window = (JSObject) webview.getEngine().executeScript("window"); - assertNotNull(window); - window.setMember("callback1", stage); - webview.getEngine().executeScript("document.getElementById(\"mybtn1\").click()"); + try { + final JSObject window = (JSObject) webview.getEngine().executeScript("window"); + assertNotNull(window); + window.setMember("callback1", stage); + webview.getEngine().executeScript("document.getElementById(\"mybtn1\").click()"); + } catch(AssertionError ex) { + encounteredException = ex; + } finally { + latch.countDown(); + } } }); @@ -326,15 +364,33 @@ } }); - Util.sleep(SLEEP_TIME); - System.gc(); - for (int i = 0; i < NUM_STAGES; i++) { - assertTrue(callbackStatus[i]); + if (encounteredException != null) { + throw encounteredException; + } + + try { + latch.await(); + } catch (InterruptedException ex) { + throw new AssertionError(ex); + } + + for (int j = 0; j < 5; j++) { + System.gc(); + System.runFinalization(); + + if (isAllCallbackStatusTrue()) { + break; + } + + Util.sleep(SLEEP_TIME); } + + assertTrue("All Button Callback return true", isAllCallbackStatusTrue()); } @Test public void testJsCallbackReleaseFunction() throws Exception { + final CountDownLatch latch = new CountDownLatch(1); Util.runAndWait(() -> { @@ -355,18 +411,24 @@ webview.getEngine().getLoadWorker().stateProperty().addListener((ov, o, n) -> { if (n == Worker.State.SUCCEEDED) { - final JSObject window = (JSObject) webview.getEngine().executeScript("window"); - assertNotNull(window); - window.setMember("callback1", stage); - window.setMember("callback4", myObj); - - webview.getEngine().executeScript("document.getElementById(\"mybtn1\").click()"); - - // Below executeScript call will make myObj=null and GC'ed - webview.getEngine().executeScript("document.getElementById(\"mybtn3\").click()"); - - // Below executeScript call should not execute the JS callback (jsobjcallback) and should not cause crash as above executeScript just made myObj=null; - webview.getEngine().executeScript("document.getElementById(\"mybtn4\").click()"); + try { + final JSObject window = (JSObject) webview.getEngine().executeScript("window"); + assertNotNull(window); + window.setMember("callback1", stage); + window.setMember("callback4", myObj); + + webview.getEngine().executeScript("document.getElementById(\"mybtn1\").click()"); + + // Below executeScript call will make myObj=null and GC'ed + webview.getEngine().executeScript("document.getElementById(\"mybtn3\").click()"); + + // Below executeScript call should not execute the JS callback (jsobjcallback) and should not cause crash as above executeScript just made myObj=null; + webview.getEngine().executeScript("document.getElementById(\"mybtn4\").click()"); + } catch(AssertionError ex) { + encounteredException = ex; + } finally { + latch.countDown(); + } } }); @@ -375,14 +437,33 @@ } }); - Util.sleep(SLEEP_TIME); - System.gc(); + if (encounteredException != null) { + throw encounteredException; + } + + try { + latch.await(); + } catch (InterruptedException ex) { + throw new AssertionError(ex); + } + + for (int j = 0; j < 5; j++) { + System.gc(); + System.runFinalization(); + + if (unexpectedCallback) { + break; + } + + Util.sleep(SLEEP_TIME); + } assertFalse(unexpectedCallback); } @Test public void testJsCallbackConsoleFunction() throws Exception { + final CountDownLatch latch = new CountDownLatch(1); Util.runAndWait(() -> { @@ -403,12 +484,19 @@ webview.getEngine().getLoadWorker().stateProperty().addListener((ov, o, n) -> { if (n == Worker.State.SUCCEEDED) { - final JSObject window = (JSObject) webview.getEngine().executeScript("window"); - assertNotNull(window); - - window.setMember("console", new Object()); - System.gc(); System.gc(); - webview.getEngine().executeScript("window.console.debug = function() {}"); + try { + final JSObject window = (JSObject) webview.getEngine().executeScript("window"); + assertNotNull(window); + + window.setMember("console", new Object()); + System.gc(); System.gc(); + System.runFinalization(); + webview.getEngine().executeScript("window.console.debug = function() {}"); + } catch(AssertionError ex) { + encounteredException = ex; + } finally { + latch.countDown(); + } } }); @@ -417,12 +505,23 @@ } }); - Util.sleep(SLEEP_TIME); + if (encounteredException != null) { + throw encounteredException; + } + + try { + latch.await(); + } catch (InterruptedException ex) { + throw new AssertionError(ex); + } + System.gc(); + System.runFinalization(); } @Test public void testJsCallbackStrongRefPrimitiveArrayFunction() throws Exception { + final CountDownLatch latch = new CountDownLatch(1); Util.runAndWait(() -> { @@ -443,12 +542,18 @@ webview.getEngine().getLoadWorker().stateProperty().addListener((ov, o, n) -> { if (n == Worker.State.SUCCEEDED) { - final JSObject window = (JSObject) webview.getEngine().executeScript("window"); - assertNotNull(window); - window.setMember("callback2", stage); - window.setMember("primitiveArray", primitiveArray); - webview.getEngine().executeScript("document.getElementById(\"mybtn2\").onclick = function() {callback2.jscallback2(primitiveArray);}"); - webview.getEngine().executeScript("document.getElementById(\"mybtn2\").click()"); + try { + final JSObject window = (JSObject) webview.getEngine().executeScript("window"); + assertNotNull(window); + window.setMember("callback2", stage); + window.setMember("primitiveArray", primitiveArray); + webview.getEngine().executeScript("document.getElementById(\"mybtn2\").onclick = function() {callback2.jscallback2(primitiveArray);}"); + webview.getEngine().executeScript("document.getElementById(\"mybtn2\").click()"); + } catch(AssertionError ex) { + encounteredException = ex; + } finally { + latch.countDown(); + } } }); @@ -457,15 +562,34 @@ } }); - Util.sleep(SLEEP_TIME); - System.gc(); - for (int i = 0; i < NUM_STAGES; i++) { - assertTrue(callbackStatus[i]); + if (encounteredException != null) { + throw encounteredException; + } + + try { + latch.await(); + } catch (InterruptedException ex) { + throw new AssertionError(ex); } + + for (int j = 0; j < 5; j++) { + System.gc(); + System.runFinalization(); + + if (isAllCallbackStatusTrue()) { + break; + } + + Util.sleep(SLEEP_TIME); + } + + assertTrue("All Button Callback return true", isAllCallbackStatusTrue()); } @Test public void testJsCallbackLocalPrimitiveArrayFunctionWithGC() throws Exception { + final CountDownLatch latch1 = new CountDownLatch(1); + final CountDownLatch latch2 = new CountDownLatch(1); Util.runAndWait(() -> { @@ -488,11 +612,18 @@ webview.getEngine().getLoadWorker().stateProperty().addListener((ov, o, n) -> { if (n == Worker.State.SUCCEEDED) { - final JSObject window = (JSObject) webview.getEngine().executeScript("window"); - assertNotNull(window); - window.setMember("callback2", stage); - window.setMember("localPrimitiveArray", new int[] { 1, 2, 3, 4, 5 }); - System.gc(); System.gc(); + try { + final JSObject window = (JSObject) webview.getEngine().executeScript("window"); + assertNotNull(window); + window.setMember("callback2", stage); + window.setMember("localPrimitiveArray", new int[] { 1, 2, 3, 4, 5 }); + System.gc(); System.gc(); + System.runFinalization(); + } catch(AssertionError ex) { + encounteredException = ex; + } finally { + latch1.countDown(); + } } }); @@ -501,23 +632,41 @@ } }); + if (encounteredException != null) { + throw encounteredException; + } + + try { + latch1.await(); + } catch (InterruptedException ex) { + throw new AssertionError(ex); + } + Util.sleep(SLEEP_TIME); - System.gc(); Util.runAndWait(() -> { for (int i = 0; i < NUM_STAGES; i++) { System.gc(); + System.runFinalization(); webviewArray[i].getEngine().executeScript("document.getElementById(\"mybtn2\").onclick = function() {callback2.jscallback3(localPrimitiveArray);}"); webviewArray[i].getEngine().executeScript("document.getElementById(\"mybtn2\").click()"); } + latch2.countDown(); }); + try { + latch2.await(); + } catch (InterruptedException ex) { + throw new AssertionError(ex); + } + assertFalse(unexpectedCallback); } @Test public void testJsCallbackStrongRefObjectArrayFunction() throws Exception { + final CountDownLatch latch = new CountDownLatch(1); Util.runAndWait(() -> { @@ -538,12 +687,18 @@ webview.getEngine().getLoadWorker().stateProperty().addListener((ov, o, n) -> { if (n == Worker.State.SUCCEEDED) { - final JSObject window = (JSObject) webview.getEngine().executeScript("window"); - assertNotNull(window); - window.setMember("callback2", stage); - window.setMember("objectArray", objectArray); - webview.getEngine().executeScript("document.getElementById(\"mybtn2\").onclick = function() {callback2.jscallback4(objectArray);}"); - webview.getEngine().executeScript("document.getElementById(\"mybtn2\").click()"); + try { + final JSObject window = (JSObject) webview.getEngine().executeScript("window"); + assertNotNull(window); + window.setMember("callback2", stage); + window.setMember("objectArray", objectArray); + webview.getEngine().executeScript("document.getElementById(\"mybtn2\").onclick = function() {callback2.jscallback4(objectArray);}"); + webview.getEngine().executeScript("document.getElementById(\"mybtn2\").click()"); + } catch(AssertionError ex) { + encounteredException = ex; + } finally { + latch.countDown(); + } } }); @@ -552,15 +707,35 @@ } }); - Util.sleep(SLEEP_TIME); - System.gc(); - for (int i = 0; i < NUM_STAGES; i++) { - assertTrue(callbackStatus[i]); + if (encounteredException != null) { + throw encounteredException; + } + + try { + latch.await(); + } catch (InterruptedException ex) { + throw new AssertionError(ex); + } + + for (int j = 0; j < 5; j++) { + System.gc(); + System.runFinalization(); + + if (isAllCallbackStatusTrue()) { + break; + } + + Util.sleep(SLEEP_TIME); } + + assertTrue("All Button Callback return true", isAllCallbackStatusTrue()); + } @Test public void testJsCallbackLocalObjectArrayFunctionWithGC() throws Exception { + final CountDownLatch latch1 = new CountDownLatch(1); + final CountDownLatch latch2 = new CountDownLatch(1); Util.runAndWait(() -> { @@ -582,11 +757,18 @@ webview.getEngine().getLoadWorker().stateProperty().addListener((ov, o, n) -> { if (n == Worker.State.SUCCEEDED) { - final JSObject window = (JSObject) webview.getEngine().executeScript("window"); - assertNotNull(window); - window.setMember("callback2", stage); - window.setMember("localObjectArray", new Object[] { new Object(), new Object(), new Object(), new Object() }); - System.gc(); System.gc(); + try { + final JSObject window = (JSObject) webview.getEngine().executeScript("window"); + assertNotNull(window); + window.setMember("callback2", stage); + window.setMember("localObjectArray", new Object[] { new Object(), new Object(), new Object(), new Object() }); + System.gc(); System.gc(); + System.runFinalization(); + } catch(AssertionError ex) { + encounteredException = ex; + } finally { + latch1.countDown(); + } } }); @@ -595,18 +777,35 @@ } }); + if (encounteredException != null) { + throw encounteredException; + } + + try { + latch1.await(); + } catch (InterruptedException ex) { + throw new AssertionError(ex); + } + Util.sleep(SLEEP_TIME); - System.gc(); Util.runAndWait(() -> { for (int i = 0; i < NUM_STAGES; i++) { System.gc(); + System.runFinalization(); webviewArray[i].getEngine().executeScript("document.getElementById(\"mybtn2\").onclick = function() {callback2.jscallback5(localObjectArray);}"); webviewArray[i].getEngine().executeScript("document.getElementById(\"mybtn2\").click()"); } + latch2.countDown(); }); + try { + latch2.await(); + } catch (InterruptedException ex) { + throw new AssertionError(ex); + } + assertFalse(unexpectedCallback); } }