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