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