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 }