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 }