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