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 }