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