1 /* 2 * Copyright (c) 2011, 2013, 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 com.sun.javafx.css; 27 28 import com.sun.javafx.FXUnit; 29 import com.sun.javafx.css.converters.EnumConverter; 30 import com.sun.javafx.css.converters.StringConverter; 31 import com.sun.javafx.css.parser.CSSParser; 32 import javafx.css.ParsedValue; 33 import javafx.css.StyleConverter; 34 import javafx.css.StyleOrigin; 35 36 import java.io.ByteArrayInputStream; 37 import java.io.ByteArrayOutputStream; 38 import java.io.DataInputStream; 39 import java.io.DataOutputStream; 40 import java.io.IOException; 41 import java.net.URL; 42 import java.util.Collections; 43 import java.util.List; 44 import java.util.Locale; 45 import java.util.Map; 46 47 import javafx.css.StyleableProperty; 48 import javafx.geometry.Orientation; 49 import javafx.geometry.Pos; 50 import javafx.geometry.VPos; 51 import javafx.scene.Group; 52 import javafx.scene.Scene; 53 import javafx.scene.layout.StackPane; 54 import javafx.scene.paint.Color; 55 import javafx.scene.paint.LinearGradient; 56 import javafx.scene.paint.Paint; 57 import javafx.scene.paint.RadialGradient; 58 import javafx.scene.shape.Rectangle; 59 import javafx.scene.text.Font; 60 import javafx.scene.text.FontSmoothingType; 61 import javafx.scene.text.TextAlignment; 62 import javafx.stage.Stage; 63 import org.junit.*; 64 import static org.junit.Assert.*; 65 import static org.junit.Assert.assertEquals; 66 67 68 public class StylesheetTest { 69 70 @org.junit.Rule 71 public final FXUnit fx = new FXUnit(); 72 73 String testURL = null; 74 75 public StylesheetTest() { 76 testURL = getClass().getResource("HonorDeveloperSettingsTest_UA.css").toExternalForm(); 77 } 78 79 /** 80 * Test of getUrl method, of class Stylesheet. 81 */ 82 @Test 83 public void testGetUrl() { 84 Stylesheet instance = new Stylesheet(); 85 URL expResult = null; 86 String result = instance.getUrl(); 87 assertEquals(expResult, result); 88 89 instance = new Stylesheet(testURL); 90 result = instance.getUrl(); 91 assertEquals(testURL, result); 92 } 93 94 /** 95 * Test of getSource method, of class Stylesheet. 96 */ 97 @Test 98 public void testGetStylesheetSourceGetterAndSetter() { 99 Stylesheet instance = new Stylesheet(); 100 StyleOrigin expResult = StyleOrigin.AUTHOR; 101 StyleOrigin result = instance.getOrigin(); 102 assertEquals(expResult, result); 103 104 instance.setOrigin(StyleOrigin.INLINE); 105 expResult = StyleOrigin.INLINE; 106 result = instance.getOrigin(); 107 assertEquals(expResult, result); 108 109 instance.setOrigin(StyleOrigin.USER); 110 expResult = StyleOrigin.USER; 111 result = instance.getOrigin(); 112 assertEquals(expResult, result); 113 114 instance.setOrigin(StyleOrigin.USER_AGENT); 115 expResult = StyleOrigin.USER_AGENT; 116 result = instance.getOrigin(); 117 assertEquals(expResult, result); 118 } 119 120 /** 121 * Test of addRule method, of class Stylesheet. 122 */ 123 @Test 124 public void testStylesheetAddAndGetRule() { 125 Rule rule = new Rule(Collections.EMPTY_LIST, Collections.EMPTY_LIST); 126 Stylesheet instance = new Stylesheet(); 127 instance.getRules().add(rule); 128 instance.getRules().add(rule); 129 instance.getRules().add(rule); 130 instance.getRules().add(rule); 131 instance.getRules().add(rule); 132 List<Rule> rules = instance.getRules(); 133 assert(rules.size() == 5); 134 for(Rule r : rules) assertEquals(r, rule); 135 } 136 137 @Test 138 public void testAddingRuleSetsStylesheetOnRule() { 139 Rule rule = new Rule(Collections.EMPTY_LIST, Collections.EMPTY_LIST); 140 Stylesheet instance = new Stylesheet(); 141 instance.getRules().add(rule); 142 assert(rule.getStylesheet() == instance); 143 } 144 145 @Test 146 public void testRemovingRuleSetsStylesheetNullOnRule() { 147 Rule rule = new Rule(Collections.EMPTY_LIST, Collections.EMPTY_LIST); 148 Stylesheet instance = new Stylesheet(); 149 instance.getRules().add(rule); 150 instance.getRules().remove(rule); 151 assertNull(rule.getStylesheet()); 152 } 153 154 /** 155 * Test of equals method, of class Stylesheet. 156 */ 157 @Test 158 public void testStylesheetEquals() { 159 Object obj = new Stylesheet(); 160 Stylesheet instance = new Stylesheet(); 161 boolean expResult = true; 162 boolean result = instance.equals(obj); 163 assertEquals(expResult, result); 164 165 obj = new Stylesheet(testURL); 166 instance = new Stylesheet(testURL); 167 expResult = true; 168 result = instance.equals(obj); 169 assertEquals(expResult, result); 170 171 obj = new Stylesheet(); 172 instance = new Stylesheet(testURL); 173 expResult = false; 174 result = instance.equals(obj); 175 assertEquals(expResult, result); 176 177 obj = instance = new Stylesheet(testURL); 178 expResult = true; 179 result = instance.equals(obj); 180 assertEquals(expResult, result); 181 182 } 183 184 /** 185 * Test of toString method, of class Stylesheet. 186 */ 187 @Test 188 public void testStylesheetToString() { 189 Stylesheet instance = new Stylesheet(); 190 String expResult = "/* */"; 191 String result = instance.toString(); 192 assertEquals(expResult, result); 193 194 instance = new Stylesheet(testURL); 195 expResult = "/* " + testURL + " */"; 196 result = instance.toString(); 197 assertEquals(expResult, result); 198 199 instance = new Stylesheet(testURL); 200 Rule rule = new Rule(Collections.EMPTY_LIST, Collections.EMPTY_LIST); 201 instance.getRules().add(rule); 202 expResult = "/* " + testURL + " */\n{\n}\n"; 203 result = instance.toString(); 204 assertEquals(expResult, result); 205 } 206 207 /** 208 * Test of writeBinary method, of class Stylesheet. 209 @Test 210 public void testWriteAndReadBinary() throws Exception { 211 DataOutputStream os = null; 212 StringStore stringStore = null; 213 Stylesheet instance = new Stylesheet(testURL); 214 instance.writeBinary(os, stringStore); 215 } 216 */ 217 218 /** 219 * Test of loadBinary method, of class Stylesheet. 220 @Test 221 public void testLoadBinary() { 222 System.out.println("loadBinary"); 223 URL url = null; 224 Stylesheet expResult = null; 225 Stylesheet result = Stylesheet.loadBinary(url); 226 assertEquals(expResult, result); 227 // TODO review the generated test code and remove the default call to fail. 228 fail("The test case is a prototype."); 229 } 230 */ 231 232 @Test public void test_RT_18126() { 233 // CSS cannot write binary -fx-background-repeat: repeat, no-repeat; 234 String data = "#rt18126 {" 235 + "-fx-background-repeat: repeat, no-repeat;" 236 + "-fx-border-image-repeat: repeat, no-repeat;" 237 + "}"; 238 239 try { 240 Stylesheet stylesheet = CSSParser.getInstance().parse(data); 241 242 StringStore stringStore = new StringStore(); 243 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 244 DataOutputStream dos = new DataOutputStream(baos); 245 stylesheet.writeBinary(dos, stringStore); 246 dos.flush(); 247 dos.close(); 248 249 ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); 250 DataInputStream dis = new DataInputStream(bais); 251 252 Stylesheet restored = new Stylesheet(); 253 restored.readBinary(Stylesheet.BINARY_CSS_VERSION, dis, stringStore.strings.toArray(new String[stringStore.strings.size()])); 254 255 List<Rule> cssRules = stylesheet.getRules(); 256 List<Rule> bssRules = restored.getRules(); 257 258 // Rule does not have an equals method 259 assert(cssRules.size() == bssRules.size()); 260 for (int n=0; n<cssRules.size(); n++) { 261 Rule expected = cssRules.get(n); 262 Rule actual = bssRules.get(n); 263 assertEquals(Integer.toString(n), expected.getUnobservedDeclarationList(), actual.getUnobservedDeclarationList()); 264 } 265 266 } catch (IOException ioe) { 267 fail(ioe.toString()); 268 } 269 270 } 271 272 @Test 273 public void testRT_23140() { 274 275 try { 276 Group root = new Group(); 277 root.getChildren().add(new Rectangle(50,50)); 278 Scene scene = new Scene(root, 500, 500); 279 root.getStylesheets().add("bogus.css"); 280 Stage stage = new Stage(); 281 stage.setScene(scene); 282 stage.show(); 283 } catch (NullPointerException e) { 284 // RT-23140 is supposed to fix the NPE. Did it? 285 fail("Test purpose failed: " + e.toString()); 286 } catch (Exception e) { 287 // Something other than an NPE should still raise a red flag, 288 // but the exception is not what RT-23140 fixed. 289 290 fail("Exception not expected: " + e.toString()); 291 } 292 293 } 294 295 @Test public void testRT_31316() { 296 297 try { 298 299 Rectangle rect = new Rectangle(50,50); 300 rect.setStyle("-fx-base: red; -fx-fill: -fx-base;"); 301 rect.setFill(Color.GREEN); 302 303 Group root = new Group(); 304 root.getChildren().add(rect); 305 Scene scene = new Scene(root, 500, 500); 306 307 root.impl_processCSS(true); 308 309 // Shows inline style works. 310 assertEquals(Color.RED, rect.getFill()); 311 312 // reset fill 313 ((StyleableProperty<Paint>)rect.fillProperty()).applyStyle(null, null); 314 315 // loop in style! 316 rect.setStyle("-fx-base: -fx-fill; -fx-fill: -fx-base;"); 317 root.impl_processCSS(true); 318 319 // Shows value was left alone 320 assertNull(rect.getFill()); 321 322 323 } catch (Exception e) { 324 // The code generates an IllegalArgumentException that should never reach here 325 fail("Exception not expected: " + e.toString()); 326 } 327 328 } 329 330 @Test public void testRT_31316_with_complex_value() { 331 332 try { 333 334 Rectangle rect = new Rectangle(50,50); 335 rect.setStyle("-fx-base: red; -fx-color: -fx-base; -fx-fill: radial-gradient(radius 100%, red, -fx-color);"); 336 rect.setFill(Color.GREEN); 337 338 Group root = new Group(); 339 root.getChildren().add(rect); 340 Scene scene = new Scene(root, 500, 500); 341 342 root.impl_processCSS(true); 343 344 // Shows inline style works. 345 assertTrue(rect.getFill() instanceof RadialGradient); 346 347 // reset fill 348 ((StyleableProperty<Paint>)rect.fillProperty()).applyStyle(null, null); 349 350 // loop in style! 351 rect.setStyle("-fx-base: -fx-color; -fx-color: -fx-base; -fx-fill: radial-gradient(radius 100%, red, -fx-color);"); 352 353 root.impl_processCSS(true); 354 355 // Shows value was left alone 356 assertNull(rect.getFill()); 357 358 } catch (Exception e) { 359 // The code generates an IllegalArgumentException that should never reach here 360 fail("Exception not expected: " + e.toString()); 361 } 362 } 363 364 365 @Test public void testRT_31316_with_complex_scenegraph() { 366 367 try { 368 369 Rectangle rect = new Rectangle(50,50); 370 rect.setStyle("-fx-fill: radial-gradient(radius 100%, red, -fx-color);"); 371 rect.setFill(Color.GREEN); 372 373 StackPane pane = new StackPane(); 374 pane.setStyle("-fx-color: -fx-base;"); 375 pane.getChildren().add(rect); 376 377 Group root = new Group(); 378 // loop in style! 379 root.setStyle("-fx-base: red;"); 380 root.getChildren().add(pane); 381 Scene scene = new Scene(root, 500, 500); 382 383 root.impl_processCSS(true); 384 385 // Shows inline style works. 386 assertTrue(rect.getFill() instanceof RadialGradient); 387 388 // reset fill 389 ((StyleableProperty<Paint>)rect.fillProperty()).applyStyle(null, null); 390 391 // loop in style 392 root.setStyle("-fx-base: -fx-color;"); 393 394 root.impl_processCSS(true); 395 396 // Shows value was left alone 397 assertNull(rect.getFill()); 398 399 } catch (Exception e) { 400 // The code generates an IllegalArgumentException that should never reach here 401 fail("Exception not expected: " + e.toString()); 402 } 403 404 } 405 406 @Test public void testRT_32229() { 407 408 try { 409 410 Rectangle rect = new Rectangle(50,50); 411 rect.setStyle("-fx-base: red; -fx-fill: radial-gradient(radius 100%, derive(-fx-base, -25%), derive(-fx-base, 25%));"); 412 rect.setFill(Color.GREEN); 413 414 Group root = new Group(); 415 root.getChildren().add(rect); 416 Scene scene = new Scene(root, 500, 500); 417 418 root.impl_processCSS(true); 419 420 // Shows inline style works. 421 assertTrue(rect.getFill() instanceof RadialGradient); 422 423 // reset fill 424 ((StyleableProperty<Paint>)rect.fillProperty()).applyStyle(null, null); 425 426 // loop in style! 427 root.setStyle("-fx-base: -fx-fill;"); 428 rect.setStyle("-fx-fill: radial-gradient(radius 100%, derive(-fx-base, -25%), derive(-fx-base, 25%));"); 429 430 431 root.impl_processCSS(true); 432 433 // Shows value was left alone 434 assertNull(rect.getFill()); 435 436 } catch (Exception e) { 437 // The code generates an IllegalArgumentException that should never reach here 438 fail("Exception not expected: " + e.toString()); 439 } 440 } 441 442 @Test 443 public void testRT_30953_parse() { 444 445 try { 446 // Make sure RT-30953.css can be parsed, serialized and deserialized with the current code, 447 // no matter the bss version 448 URL url = StylesheetTest.class.getResource("RT-30953.css"); 449 if (url == null) { 450 fail("Can't find RT-30953.css"); 451 } 452 453 Stylesheet ss = CSSParser.getInstance().parse(url); 454 int nFontFaceSrcs = checkFontFace(ss); 455 assertEquals(3, nFontFaceSrcs); 456 checkConvert(ss); 457 458 } catch (Exception e) { 459 fail(e.toString()); 460 } 461 462 } 463 464 @Test public void testRT_30953_deserialize_from_v4() { 465 // RT-30953-v4.bss was generated with version 4 466 Stylesheet ss = deserialize("RT-30953-v4.bss"); 467 checkConvert(ss); 468 } 469 470 @Test 471 public void testRT_30953_deserialize_from_2_2_45() { 472 473 // RT-30953-2.2.4bss was generated with javafx version 2.2.45 from 7u?? 474 Stylesheet ss = deserialize("RT-30953-2.2.45.bss"); 475 checkConvert(ss); 476 } 477 478 @Test 479 public void testRT_30953_deserialize_from_2_2_4() { 480 481 // RT-30953-2.2.4bss was generated with javafx version 2.2.4 from 7u10 482 Stylesheet ss = deserialize("RT-30953-2.2.4.bss"); 483 checkConvert(ss); 484 } 485 486 @Test 487 public void testRT_30953_deserialize_from_2_2_21() { 488 489 // RT-30953-2.2.21.bss was generated with javafx version 2.2.21 from 7u21 490 Stylesheet ss = deserialize("RT-30953-2.2.21.bss"); 491 checkConvert(ss); 492 493 } 494 495 private Stylesheet deserialize(String bssFile) { 496 Stylesheet ss = null; 497 try { 498 URL url = StylesheetTest.class.getResource(bssFile); 499 if (url == null) { 500 fail(bssFile); 501 } 502 ss = Stylesheet.loadBinary(url); 503 } catch (IOException ioe) { 504 fail(ioe.toString()); 505 } catch (Exception e) { 506 fail(e.toString()); 507 } 508 return ss; 509 } 510 511 private void checkConvert(Stylesheet ss) { 512 Declaration decl = null; 513 StyleConverter converter = null; 514 try { 515 for (Rule r : ss.getRules()) { 516 for (Declaration d : r.getDeclarations()) { 517 decl = d; 518 ParsedValue pv = decl.getParsedValue(); 519 converter = pv.getConverter(); 520 if (converter == null) { 521 522 if ("inherit".equals(pv.getValue())) continue; 523 524 String prop = d.getProperty().toLowerCase(Locale.ROOT); 525 if ("-fx-shape".equals(prop)) { 526 StringConverter.getInstance().convert(pv, null); 527 } else if ("-fx-font-smoothing-type".equals(prop)) { 528 (new EnumConverter<FontSmoothingType>(FontSmoothingType.class)).convert(pv, null); 529 } else if ("-fx-text-alignment".equals(prop)) { 530 (new EnumConverter<TextAlignment>(TextAlignment.class)).convert(pv, null); 531 } else if ("-fx-alignment".equals(prop)) { 532 (new EnumConverter<Pos>(Pos.class)).convert(pv, null); 533 } else if ("-fx-text-origin".equals(prop)) { 534 (new EnumConverter<VPos>(VPos.class)).convert(pv, null); 535 } else if ("-fx-text-overrun".equals(prop)) { 536 Class cl = null; 537 try { 538 cl = Class.forName("javafx.scene.control.OverrunStyle"); 539 } catch (Exception ignored) { 540 // just means we're running ant test from javafx-ui-common 541 } 542 if (cl != null) { 543 (new EnumConverter(cl)).convert(pv, null); 544 } 545 } else if ("-fx-orientation".equals(prop)) { 546 (new EnumConverter<Orientation>(Orientation.class)).convert(pv, null); 547 } else if ("-fx-content-display".equals(prop)) { 548 Class cl = null; 549 try { 550 cl = Class.forName("javafx.scene.control.CpntentDisplay"); 551 } catch (Exception ignored) { 552 // just means we're running ant test from javafx-ui-common 553 } 554 if (cl != null) { 555 (new EnumConverter(cl)).convert(pv, null); 556 } 557 } else if ("-fx-hbar-policy".equals(prop)) { 558 Class cl = null; 559 try { 560 cl = Class.forName("javafx.scene.control.ScrollPane.ScrollBarPolicy"); 561 } catch (Exception ignored) { 562 // just means we're running ant test from javafx-ui-common 563 } 564 if (cl != null) { 565 (new EnumConverter(cl)).convert(pv, null); 566 } 567 } else { 568 System.out.println("No converter for " + d.toString() + ". Skipped conversion."); 569 } 570 continue; 571 } 572 Object value = converter.convert(pv, Font.getDefault()); 573 } 574 } 575 } catch (Exception e) { 576 if (decl == null) fail(e.toString()); 577 else if (converter != null) fail(decl.getProperty() + ", " + converter + ", " + e.toString()); 578 else fail(decl.getProperty() + ", " + e.toString()); 579 } 580 581 } 582 583 private int checkFontFace(Stylesheet stylesheet) { 584 return com.sun.javafx.css.parser.CSSParserTest.checkFontFace(stylesheet); 585 } 586 587 }