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