1 /* 2 * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package test.javafx.scene.web; 27 28 import java.io.File; 29 import java.util.concurrent.Callable; 30 import java.util.concurrent.CountDownLatch; 31 import java.util.concurrent.FutureTask; 32 import java.util.concurrent.atomic.AtomicBoolean; 33 34 import javafx.application.Platform; 35 import javafx.beans.InvalidationListener; 36 import javafx.beans.Observable; 37 import javafx.beans.value.ChangeListener; 38 import javafx.beans.value.ObservableValue; 39 import javafx.concurrent.Worker; 40 41 import com.sun.javafx.application.PlatformImpl; 42 import java.util.concurrent.ExecutionException; 43 import javafx.scene.web.WebEngine; 44 import javafx.scene.web.WebView; 45 import org.junit.BeforeClass; 46 import org.w3c.dom.Document; 47 48 public class TestBase implements ChangeListener, InvalidationListener { 49 private static final AtomicBoolean LOCK = new AtomicBoolean(false); 50 private static final int INIT_TIMEOUT = 10000; 51 private static final int LOAD_TIMEOUT = 60000; 52 53 private static WebView view; 54 55 @BeforeClass 56 public static void setupOnce() { 57 final CountDownLatch startupLatch = new CountDownLatch(1); 58 59 PlatformImpl.startup(() -> { 60 startupLatch.countDown(); 61 }); 62 63 try { 64 startupLatch.await(); 65 } catch (InterruptedException ex) {} 66 } 67 68 public TestBase() { 69 Platform.runLater(() -> { 70 view = new WebView(); 71 WebEngine web = view.getEngine(); 72 73 web.documentProperty().addListener((ChangeListener)TestBase.this); 74 web.documentProperty().addListener((InvalidationListener)TestBase.this); 75 web.titleProperty().addListener((ChangeListener)TestBase.this); 76 web.titleProperty().addListener((InvalidationListener)TestBase.this); 77 web.locationProperty().addListener((ChangeListener)TestBase.this); 78 web.locationProperty().addListener((InvalidationListener)TestBase.this); 79 80 Worker loadTask = web.getLoadWorker(); 81 loadTask.exceptionProperty().addListener((ChangeListener)TestBase.this); 82 loadTask.exceptionProperty().addListener((InvalidationListener)TestBase.this); 83 loadTask.messageProperty().addListener((ChangeListener)TestBase.this); 84 loadTask.messageProperty().addListener((InvalidationListener)TestBase.this); 85 loadTask.progressProperty().addListener((ChangeListener)TestBase.this); 86 loadTask.progressProperty().addListener((InvalidationListener)TestBase.this); 87 loadTask.runningProperty().addListener((ChangeListener)TestBase.this); 88 loadTask.runningProperty().addListener((InvalidationListener)TestBase.this); 89 loadTask.stateProperty().addListener((ChangeListener)TestBase.this); 90 loadTask.stateProperty().addListener((InvalidationListener)TestBase.this); 91 loadTask.titleProperty().addListener((ChangeListener)TestBase.this); 92 loadTask.titleProperty().addListener((InvalidationListener)TestBase.this); 93 loadTask.totalWorkProperty().addListener((ChangeListener)TestBase.this); 94 loadTask.totalWorkProperty().addListener((InvalidationListener)TestBase.this); 95 loadTask.valueProperty().addListener((ChangeListener)TestBase.this); 96 loadTask.valueProperty().addListener((InvalidationListener)TestBase.this); 97 loadTask.workDoneProperty().addListener((ChangeListener)TestBase.this); 98 loadTask.workDoneProperty().addListener((InvalidationListener)TestBase.this); 99 100 loadTask.runningProperty().addListener(new LoadFinishedListener()); 101 102 TestBase.this.notify(LOCK); 103 }); 104 105 wait(LOCK, INIT_TIMEOUT); 106 } 107 108 /** 109 * Loads content from a URL. 110 * This method blocks until loading is finished. 111 */ 112 protected void load(final String url) { 113 Platform.runLater(() -> { 114 getEngine().load(url); 115 }); 116 waitLoadFinished(); 117 } 118 119 /** 120 * Reloads current page. 121 * This method blocks until loading is finished. 122 */ 123 protected void reload() { 124 Platform.runLater(() -> { 125 getEngine().reload(); 126 }); 127 waitLoadFinished(); 128 } 129 130 /** 131 * Loads content from a file. 132 * This method blocks until loading is finished. 133 */ 134 protected void load(File file) { 135 load(file.toURI().toASCIIString()); 136 } 137 138 /** 139 * Loads content from a file, and returns the resulting document. 140 * This method blocks until loading is finished. 141 */ 142 protected Document getDocumentFor(String fileName) { 143 load(new File(fileName)); 144 return getEngine().getDocument(); 145 } 146 147 /** 148 * Loads content of the specified type from a String. 149 * This method does not return until loading is finished. 150 */ 151 protected void loadContent(final String content, final String contentType) { 152 Platform.runLater(() -> { 153 getEngine().loadContent(content, contentType); 154 }); 155 waitLoadFinished(); 156 } 157 158 /** 159 * Loads HTML content from a String. 160 * This method does not return until loading is finished. 161 */ 162 protected void loadContent(final String content) { 163 loadContent(content, "text/html"); 164 } 165 166 /** 167 * Executes a job on FX thread, and waits until it is complete. 168 */ 169 protected void submit(Runnable job) { 170 final FutureTask<Void> future = new FutureTask<Void>(job, null); 171 Platform.runLater(future); 172 try { 173 // block until job is complete 174 future.get(); 175 } catch (ExecutionException e) { 176 Throwable cause = e.getCause(); 177 // rethrow any assertion errors as is 178 if (cause instanceof AssertionError) { 179 throw (AssertionError) e.getCause(); 180 } else if (cause instanceof RuntimeException) { 181 throw (RuntimeException) cause; 182 } 183 // any other exception should be considered a test error 184 throw new AssertionError(cause); 185 } catch (InterruptedException e) { 186 throw new AssertionError(e); 187 } 188 } 189 190 /** 191 * Executes a job on FX thread, waits until completion, and returns its result. 192 */ 193 protected <T> T submit(Callable<T> job) { 194 final FutureTask<T> future = new FutureTask<T>(job); 195 Platform.runLater(future); 196 try { 197 return future.get(); 198 } catch (ExecutionException e) { 199 Throwable cause = e.getCause(); 200 // rethrow any assertion errors as is 201 if (cause instanceof AssertionError) { 202 throw (AssertionError) e.getCause(); 203 } 204 // any other exception should be considered a test error 205 throw new AssertionError(cause); 206 } catch (InterruptedException e) { 207 throw new AssertionError(e); 208 } 209 } 210 211 /** 212 * Executes a script. 213 * This method does not return until execution is complete. 214 */ 215 protected Object executeScript(final String script) { 216 return submit(() -> getEngine().executeScript(script)); 217 } 218 219 private class LoadFinishedListener implements ChangeListener<Boolean> { 220 @Override 221 public void changed(ObservableValue<? extends Boolean> observable, 222 Boolean oldValue, Boolean newValue) { 223 if (! newValue) { 224 TestBase.this.notify(LOCK); 225 } 226 } 227 } 228 229 private void wait(AtomicBoolean condition, long timeout) { 230 synchronized (condition) { 231 long startTime = System.currentTimeMillis(); 232 while (!condition.get()) { 233 try { 234 condition.wait(timeout); 235 } catch (InterruptedException e) { 236 } finally { 237 if (System.currentTimeMillis() - startTime >= timeout) { 238 throw new AssertionError("Waiting timed out"); 239 } 240 } 241 } 242 condition.set(false); 243 } 244 } 245 246 private void notify(AtomicBoolean condition) { 247 synchronized (condition) { 248 condition.set(true); 249 condition.notifyAll(); 250 } 251 } 252 253 /** 254 * Override this to get loading notifications from both WebEngine 255 * and its loadWorker. 256 */ 257 @Override public void invalidated(Observable value) { 258 } 259 260 /** 261 * Override this to get loading notifications from both WebEngine 262 * and its loadWorker. 263 */ 264 @Override public void changed(ObservableValue value, Object oldValue, Object newValue) { 265 } 266 267 /** 268 * Returns the WebEngine object under test. 269 */ 270 protected WebEngine getEngine() { 271 return view.getEngine(); 272 } 273 274 /** 275 * Returns the WebView object under test. 276 */ 277 protected WebView getView() { 278 return view; 279 } 280 281 /** 282 * Allows to override default load timeout value (in milliseconds). 283 */ 284 protected int getLoadTimeOut() { 285 return LOAD_TIMEOUT; 286 } 287 288 public void waitLoadFinished() { 289 wait(LOCK, getLoadTimeOut()); 290 } 291 292 /** 293 * Check for Jigsaw Mode 294 */ 295 public boolean isJigsawMode() { 296 Class clazz = null; 297 try { 298 clazz = Class.forName("java.lang.reflect.ModuleDescriptor", false, TestBase.class.getClassLoader()); 299 } catch (Exception e) { } 300 301 return clazz != null; 302 } 303 }