1 /* 2 * Copyright (c) 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 package test.fxmltests.functional; 26 27 import javafx.scene.Parent; 28 import javafx.scene.control.Label; 29 import junit.framework.Assert; 30 import org.jemmy.control.Wrap; 31 import org.jemmy.env.Environment; 32 import org.jemmy.fx.NodeDock; 33 import org.jemmy.fx.SceneDock; 34 import org.jemmy.fx.control.LabeledDock; 35 import org.jemmy.image.GlassImage; 36 import org.jemmy.image.Image; 37 import org.jemmy.timing.State; 38 import org.junit.Test; 39 import org.junit.BeforeClass; 40 import test.fxmltests.app.NoArgsController; 41 import test.fxmltests.app.OverloadedController; 42 43 import test.javaclient.shared.TestBase; 44 import test.fxmltests.app.staticPropertyLoadApp; 45 import static test.fxmltests.app.staticPropertyLoadApp.Pages; 46 import static test.javaclient.shared.TestUtil.isEmbedded; 47 48 public class staticPropertyLoadTest extends TestBase { 49 50 /** 51 * 52 * test HashMap reading 53 * @testableAssertId map_instantiaton 54 */ 55 @Test 56 public void HashMap() throws InterruptedException { 57 testCommon(Pages.HashMap.name(),null,false,true); 58 } 59 60 /** 61 * 62 * "load()" method smoke test 63 * @testableAssertId instantiation_builder 64 */ 65 @Test 66 public void simple() throws InterruptedException { 67 testCommon(Pages.simple.name(),null,false,true); 68 } 69 70 /** 71 * 72 * test fx:copy feature 73 * @testableAssertId copy 74 */ 75 @Test 76 public void copy() throws InterruptedException { 77 testCommon(Pages.copy.name(),null,false,true); 78 } 79 80 /** 81 * 82 * test "include" feature 83 * @testableAssertId include_no_leading_slash_character 84 */ 85 @Test 86 public void customClassAndInclude() throws InterruptedException { 87 testCommon(Pages.customClassAndInclude.name(),null,false,true); 88 } 89 90 /** 91 * 92 * test "default property" feature 93 */ 94 @Test 95 public void defaultProperty() throws InterruptedException { 96 testCommon(Pages.defaultProperty.name(),null,false,true); 97 } 98 99 /** 100 * 101 * test factory class usage 102 * @testableAssertId instantiation_factory 103 */ 104 @Test 105 public void factory() throws InterruptedException { 106 testCommon(Pages.factory.name(),null,false,true); 107 } 108 109 /** 110 * 111 * test fx:define feature 112 * @testableAssertId define 113 */ 114 @Test 115 public void fxdefine() throws InterruptedException { 116 testCommon(Pages.fxdefine.name(),null,false,true); 117 } 118 119 /** 120 * 121 * test fxml namespace binding feature 122 * @testableAssertId controller_method_event_handler_specification 123 */ 124 @Test 125 public void namespaceBinding() throws InterruptedException { 126 testCommon(Pages.namespaceBinding.name(),null,false,true); 127 } 128 129 /** 130 * 131 * test property setter feature 132 * @testableAssertId property_setter 133 */ 134 @Test 135 public void prSetter() throws InterruptedException { 136 testCommon(Pages.prSetter.name(),null,false,true); 137 } 138 139 /** 140 * 141 * test property element reading 142 * @testableAssertId instance_properties 143 */ 144 @Test 145 public void propertyElements() throws InterruptedException { 146 testCommon(Pages.propertyElements.name(),null,false,true); 147 } 148 149 /** 150 * 151 * test fx:reference> element 152 * @testableAssertId reference 153 */ 154 @Test 155 public void reference() throws InterruptedException { 156 testCommon(Pages.reference.name(),null,false,true); 157 } 158 159 /** 160 * 161 * test read-only list reading feature 162 * @testableAssertId read_only_list_property 163 */ 164 @Test 165 public void roList() throws InterruptedException { 166 testCommon(Pages.roList.name(),null,false,true); 167 } 168 169 /** 170 * 171 * test read-only map reading feature 172 * @testableAssertId read_only_map_property 173 */ 174 @Test 175 public void roMap() throws InterruptedException { 176 testCommon(Pages.roMap.name(),null,false,true); 177 } 178 179 /** 180 * 181 * test root method event handler 182 */ 183 @Test 184 public void rootMethodEventHandler() throws InterruptedException { 185 testCommon(Pages.rootMethodEventHandler.name(),null,false,true); 186 } 187 188 /** 189 * 190 * test script handler 191 * @testableAssertId script_event_handlers 192 */ 193 @Test 194 public void scriptEventHandler() throws InterruptedException { 195 testCommon(Pages.scriptEventHandler.name(),null,false,true); 196 } 197 198 /** 199 * 200 * test "static" property represented as xml attribute 201 * @testableAssertId static_property_attributes 202 */ 203 @Test 204 public void staticProperties() throws InterruptedException { 205 testCommon(Pages.staticProperties.name(),null,false,true); 206 } 207 208 /** 209 * 210 * test "static" property represented as xml element 211 * @testableAssertId static_property 212 */ 213 @Test 214 public void staticProperty() throws InterruptedException { 215 testCommon(Pages.staticProperty.name(),null,false,true); 216 } 217 218 /** 219 * 220 * dedicated test for http://javafx-jira.kenai.com/browse/RT-17715 221 */ 222 @Test 223 public void menuItem() throws InterruptedException { 224 testCommon(Pages.menuItem.name(),null,false,true); 225 } 226 227 /** 228 * 229 * test fxmlLoader.resources() method (part 1) 230 */ 231 @Test 232 public void resources1() throws InterruptedException { 233 testCommon(Pages.resources1.name(),null,false,true); 234 } 235 236 /** 237 * 238 * test fxmlLoader.resources() method (part 2) 239 * @testableAssertId resource_resolution 240 */ 241 @Test 242 public void resources2() throws InterruptedException { 243 testCommon(Pages.resources2.name(),null,false,true); 244 } 245 246 /** 247 * 248 * test fxmlLoader.getNamespace() method 249 */ 250 @Test 251 public void namespace() throws InterruptedException { 252 testCommon(Pages.namespace.name(),null,false,true); 253 } 254 255 /** 256 * 257 * dedicated test for http://javafx-jira.kenai.com/browse/RT-16977 258 */ 259 @Test 260 public void splitpanebug() throws InterruptedException { 261 testCommon(Pages.splitpanebug.name(),null,false,true); 262 } 263 264 /** 265 * 266 * test reading xml with non-default encoding 267 */ 268 @Test 269 public void charset() throws InterruptedException { 270 testCommon(Pages.charset.name(),null,false,true); 271 } 272 273 /** 274 * 275 * test exceptions throwing during xml load 276 */ 277 @Test 278 public void exceptions() throws InterruptedException { 279 testCommon(Pages.loadExceptions.name(),null,false,true); 280 } 281 282 @Test 283 public void slotDefaultLabeled() throws InterruptedException { 284 testCommon(Pages.slotDefaultLabeled.name(),null,false,true); 285 } 286 287 /** 288 * 289 * test @DefaultProperty in ListView class 290 */ 291 @Test 292 public void slotDefaultListview() throws InterruptedException { 293 testCommon(Pages.slotDefaultListview.name(),null,false,true); 294 } 295 296 /** 297 * 298 * test @DefaultProperty in Menu class 299 */ 300 @Test 301 public void slotDefaultMenu() throws InterruptedException { 302 testCommon(Pages.slotDefaultMenu.name(),null,false,true); 303 } 304 305 /** 306 * 307 * test @DefaultProperty in ListView 308 */ 309 @Test 310 public void slotDefaultMenuBar() throws InterruptedException { 311 testCommon(Pages.slotDefaultMenuBar.name(),null,false,true); 312 } 313 314 /** 315 * 316 * test @DefaultProperty in ScrollPane 317 */ 318 @Test 319 public void slotDefaultScrollpane() throws InterruptedException { 320 testCommon(Pages.slotDefaultScrollpane.name(),null,false,true); 321 } 322 323 /** 324 * 325 * test @DefaultProperty in TabPane 326 */ 327 @Test 328 public void slotDefaultTabpane() throws InterruptedException { 329 testCommon(Pages.slotDefaultTabpane.name(),null,false,true); 330 } 331 332 /** 333 * 334 * test @DefaultProperty in TableView 335 */ 336 @Test 337 public void slotDefaultTableView() throws InterruptedException { 338 testCommon(Pages.slotDefaultTableView.name(),null,false,true); 339 } 340 341 /** 342 * 343 * test @DefaultProperty in TextInput 344 */ 345 @Test 346 public void slotDefaultTextinput() throws InterruptedException { 347 testCommon(Pages.slotDefaultTextinput.name(),null,false,true); 348 } 349 350 /** 351 * 352 * test @DefaultProperty in TitledPane 353 */ 354 @Test 355 public void slotDefaultTitledpane() throws InterruptedException { 356 testCommon(Pages.slotDefaultTitledpane.name(),null,false,true); 357 } 358 359 /** 360 * 361 * test @DefaultProperty in TreeView 362 */ 363 @Test 364 public void slotDefaultTreeview() throws InterruptedException { 365 testCommon(Pages.slotDefaultTreeview.name(),null,false,true); 366 } 367 368 /** 369 * 370 * test @DefaultProperty in ImageView 371 * @testableAssertId location_resolution 372 */ 373 @Test 374 public void slotDefaultImageview() throws InterruptedException { 375 testCommon(Pages.slotDefaultImageview.name(),null,false,true); 376 } 377 378 /** 379 * 380 * test @DefaultProperty in Pane 381 * @testableAssertId default_property 382 */ 383 @Test 384 public void slotDefaultPane() throws InterruptedException { 385 testCommon(Pages.slotDefaultPane.name(),null,false,true); 386 } 387 388 /** 389 * 390 * test custom controller factory usage 391 */ 392 @Test 393 public void slotControllerFactory() throws InterruptedException { 394 testCommon(Pages.slotControllerFactory.name(),null,false,true); 395 } 396 397 /** 398 * 399 * test handler selection based on method signature 400 */ 401 @Test 402 public void slotSpecificSignatureMethod() throws InterruptedException { 403 testCommon(Pages.specificSignatureMethod.name(),null,false,true); 404 } 405 406 /** 407 * 408 * fxml fontbuilder test 409 */ 410 @Test 411 public void loadfontSlot() throws InterruptedException { 412 testCommon(Pages.loadfontSlot.name(),null,false,true); 413 } 414 415 /** 416 * 417 * fxml scenebuilder test 418 * @testableAssertId expression_binding 419 */ 420 @Test 421 public void scenebuilderSlot() throws InterruptedException { 422 testCommon(Pages.scenebuilderSlot.name(),null,false,true); 423 } 424 425 426 /** 427 * 428 * fxml RT-18178 test 429 */ 430 @Test 431 public void references2Slot() throws InterruptedException { 432 testCommon(Pages.references2.name(),null,false,true); 433 } 434 435 /** 436 * 437 * fxml http://javafx-jira.kenai.com/browse/RT-19112 test 438 */ 439 @Test 440 public void apConstraintsSlot() throws InterruptedException { 441 testCommon(Pages.apConstraints.name(),null,false,true); 442 } 443 444 /** 445 * 446 * fxml http://javafx-jira.kenai.com/browse/RT-19142 test 447 * @testableAssertId coercion 448 */ 449 @Test 450 public void coerceSlot() throws InterruptedException { 451 testCommon(Pages.coerce.name(),null,false,true); 452 } 453 454 /** 455 * 456 * fxml http://javafx-jira.kenai.com/browse/RT-19139 test 457 */ 458 @Test 459 public void listviewItemsSlot() throws InterruptedException { 460 testCommon(Pages.listviewItems.name(),null,false,true); 461 } 462 463 /** 464 * 465 * fxml http://javafx-jira.kenai.com/browse/RT-19752 test 466 */ 467 @Test 468 public void gridappletSlot() throws InterruptedException { 469 testCommon(Pages.gridapplet.name(),null,false,true); 470 } 471 472 /** 473 * 474 * fxml http://javafx-jira.kenai.com/browse/RT-18956 test 475 */ 476 /* 477 @Test 478 public void csspathSlot() throws InterruptedException { 479 testCommon(Pages.csspath.name(),null,false,true); 480 } 481 * 482 */ 483 484 /** 485 * 486 * fxml http://javafx-jira.kenai.com/browse/RT-18299 test 487 */ 488 @Test 489 public void scenebuilderNoRootSlot() throws InterruptedException { 490 testCommon(Pages.scenebuilderNoRoot.name(),null,false,true); 491 } 492 493 /** 494 * 495 * fxml http://javafx-jira.kenai.com/browse/RT-14345 test 496 */ 497 @Test 498 public void colorValueOfSlot() throws InterruptedException { 499 testCommon(Pages.colorValueOF.name(),null,false,true); 500 } 501 502 /** 503 * 504 * fxml http://javafx-jira.kenai.com/browse/RT-18680 test 505 * @testableAssertId escape_sequences 506 */ 507 @Test 508 public void escapeCharacterSlot() throws InterruptedException { 509 testCommon(Pages.escapeCharacter.name(),null,false,true); 510 } 511 512 /** 513 * 514 * fxml http://javafx-jira.kenai.com/browse/RT-17714 test 515 */ 516 @Test 517 public void collectionEventsSlot() throws InterruptedException { 518 testCommon(Pages.collectionEventsHandlers.name(),null,false,true); 519 } 520 521 /** 522 * 523 * fxml http://javafx-jira.kenai.com/browse/RT-18229 test 524 * @testableAssertId handler_method_opt_arg 525 */ 526 @Test 527 public void noArgsControllerMethod() throws InterruptedException { 528 testCommon(Pages.noArgControllerMethod.name(),null,false,true); 529 int count = NoArgsController.counter; 530 LabeledDock ld = new LabeledDock(new SceneDock(getScene()).asParent(), "btn_no_args_handler"); 531 ld.mouse().click(); 532 ld.wrap().waitState(new State<Integer>() { 533 534 @Override 535 public Integer reached() { 536 return NoArgsController.counter; 537 } 538 }, count + 1); 539 } 540 541 /** 542 * 543 * fxml http://javafx-jira.kenai.com/browse/RT-18229 test 544 */ 545 @Test 546 public void overloadedControllerMethod() throws InterruptedException { 547 testCommon(Pages.overloadedControllerMethod.name(),null,false,true); 548 int noArgsCount = OverloadedController.getNoArgsCounter(); 549 int eventCount = OverloadedController.getEventCounter(); 550 LabeledDock ld = new LabeledDock(new SceneDock(getScene()).asParent(), "btn_overloaded_handler"); 551 ld.mouse().click(); 552 ld.wrap().waitState(new State<Integer>() { 553 554 @Override 555 public Integer reached() { 556 return OverloadedController.getNoArgsCounter(); 557 } 558 }, noArgsCount); 559 ld.wrap().waitState(new State<Integer>() { 560 561 @Override 562 public Integer reached() { 563 return OverloadedController.getEventCounter(); 564 } 565 }, eventCount + 1); 566 } 567 568 /** 569 * 570 * fxml http://javafx-jira.kenai.com/browse/RT-18218 test 571 */ 572 @Test 573 public void unknownCustopTypesSlot() throws InterruptedException { 574 testCommon(Pages.unknownCustomType.name(),null,false,true); 575 final LabeledDock ld = new LabeledDock(new SceneDock(getScene()).asParent(), 576 staticPropertyLoadApp.LOADER_LOG_ID); 577 ld.wrap().waitState(new State<String>() { 578 579 @Override 580 public String reached() { 581 return ld.getText(); 582 } 583 }, staticPropertyLoadApp.SUCCESSFUL_STATIC_LOAD); 584 } 585 586 /** 587 * 588 * fxml http://javafx-jira.kenai.com/browse/RT-19329 test 589 */ 590 @Test 591 public void fxmlErrorSlot() throws InterruptedException { 592 testCommon(Pages.errorFXML.name(),null,false,true); 593 } 594 595 /** 596 * 597 * fxml http://javafx-jira.kenai.com/browse/RT-16722 test 598 */ 599 @Test 600 public void baseControllerPropertySlot() throws InterruptedException { 601 testCommon(Pages.baseControllerProperty.name(),null,false,true); 602 final LabeledDock ld1 = new LabeledDock(new SceneDock(getScene()).asParent(), 603 "button1_base_controller"); 604 final LabeledDock ld2 = new LabeledDock(new SceneDock(getScene()).asParent(), 605 "button2_child_controller"); 606 ld1.mouse().click(); 607 ld2.mouse().click(); 608 ld1.wrap().waitState(new State<String>() { 609 610 @Override 611 public String reached() { 612 return ld1.getText(); 613 } 614 }, "button1BaseHandler"); 615 ld2.wrap().waitState(new State<String>() { 616 617 @Override 618 public String reached() { 619 return ld2.getText(); 620 } 621 }, "button2ChildHandler"); 622 } 623 624 /** 625 * 626 * fxml http://javafx-jira.kenai.com/browse/RT-18956 test 627 */ 628 @Test 629 public void relativeStylesheetsSlot() throws InterruptedException { 630 testCommon(Pages.relativeStylesheet.name(),null,false,true); 631 final LabeledDock ld = new LabeledDock(new SceneDock(getScene()).asParent(), 632 "relative-stylesheet-label"); 633 ld.wrap().waitState(new State<String>() { 634 635 @Override 636 public String reached() { 637 boolean isRed = false; 638 Image image = ld.wrap().getScreenImage(); 639 if (image instanceof org.jemmy.image.AWTImage) { 640 final double[] vv = test.javaclient.shared.SwingAWTUtils.getColors(image); 641 System.err.println("="); 642 for (double n : vv){ 643 System.err.print("/" + n + "/"); 644 } 645 if ((3==vv.length) && (255==vv[0]) && (0==vv[1]) && (0==vv[2])) 646 isRed = true; 647 } else { 648 System.err.println("Glass! image!"); 649 final double[] vv = new double[((GlassImage)image).getSupported().length]; 650 ((GlassImage)image).getColors(((GlassImage)image).getSize().width /2, ((GlassImage)image).getSize().height/2, vv); 651 System.err.println("="); 652 for (double n : vv){ 653 System.err.print("/" + n + "/"); 654 } 655 if ((4==vv.length) && (0==vv[0]) && (0==vv[1]) && (1==vv[2]) && (1==vv[3])) 656 isRed = true; 657 658 } 659 660 661 return isRed?"red":"nonred"; 662 } 663 }, "red"); 664 } 665 666 667 /** 668 * 669 * fxml http://javafx-jira.kenai.com/browse/RT-16815 test 670 */ 671 @Test 672 public void FXMLLoaderAPISlot() throws InterruptedException { 673 testCommon(Pages.FXMLLoaderAPI.name(),null,false,true); 674 } 675 676 /** 677 * 678 * @testableAssertId fully_qualified_name 679 */ 680 @Test 681 public void FullyQualifiedNamesSlot() throws InterruptedException 682 { 683 testCommon(Pages.fullyQualifiedNames.name(),null,false,true); 684 LabeledDock ld = new LabeledDock(new SceneDock(getScene()).asParent(), "fully-qualified-label-id"); 685 Assert.assertTrue(ld.control() instanceof Label); 686 } 687 688 /** 689 * 690 * @testableAssertId import 691 */ 692 @Test 693 public void ImportSlot() throws InterruptedException 694 { 695 testCommon(Pages.importPI.name(),null,false,true); 696 LabeledDock ld = new LabeledDock(new SceneDock(getScene()).asParent(), "import-label-id"); 697 Assert.assertTrue(ld.control() instanceof Label); 698 } 699 700 /** 701 * 702 * @testableAssertId instantiation_java_bean 703 */ 704 @Test 705 public void BeanInstantiationSlot() throws InterruptedException 706 { 707 testCommon(Pages.beanInstantiation.name(),null,false,true); 708 LabeledDock ld = new LabeledDock(new SceneDock(getScene()).asParent(), "label_from_bean"); 709 Assert.assertEquals(ld.getText(), "Just string"); 710 } 711 712 /** 713 * 714 * @testableAssertId instantiation_no_default_constructor 715 */ 716 @Test 717 public void ValueOfSlot() throws InterruptedException 718 { 719 testCommon(Pages.valueOf.name(),null,false,true); 720 LabeledDock ld = new LabeledDock(new SceneDock(getScene()).asParent(), "value_of_label"); 721 Assert.assertEquals(ld.getText(), "1.0"); 722 } 723 724 /** 725 * 726 * @testableAssertId root 727 */ 728 @Test 729 public void RootSlot() throws InterruptedException 730 { 731 testCommon(Pages.root.name(),null,false,true); 732 NodeDock vBoxDock = new NodeDock(new SceneDock(getScene()).asParent(), "vb_root"); 733 LabeledDock ld = new LabeledDock(new SceneDock(getScene()).asParent(), "boxed_label"); 734 Assert.assertEquals(ld.control().getParent(), vBoxDock.control()); 735 } 736 737 /** 738 * 739 * @testableAssertId assigning_id 740 */ 741 @Test 742 public void FxIdSlot() throws InterruptedException 743 { 744 testCommon(Pages.fxId.name(),null,false,true); 745 } 746 747 /** 748 * 749 * @testableAssertId controller_attribute 750 */ 751 @Test 752 public void FxControllerSlot() throws InterruptedException 753 { 754 testCommon(Pages.fxController.name(),null,false,true); 755 } 756 757 /** 758 * 759 * @testableAssertId loader_setRoot 760 */ 761 @Test 762 public void setRootSlot() throws InterruptedException 763 { 764 testCommon(Pages.root.name(),null,false,true); 765 NodeDock node = new NodeDock(new SceneDock(getScene()).asParent(), "boxed_label"); 766 Parent root = node.control().getParent(); 767 Assert.assertEquals("vb_root", root.getId()); 768 } 769 770 /** 771 * 772 * @testableAssertId loader_setController 773 */ 774 @Test 775 public void setControllerSlot() throws InterruptedException 776 { 777 testCommon(Pages.noDefaultController.name(),null,false,true); 778 } 779 780 /** 781 * 782 * @testableAssertId loader_setRoot_sequence 783 */ 784 @Test 785 public void setRootSequenceSlot() throws InterruptedException 786 { 787 testCommon(Pages.lateRoot.name(),null,false,true); 788 } 789 790 /** 791 * 792 * @testableAssertId loader_setController_sequence 793 */ 794 @Test 795 public void setControllerSequenceSlot() throws InterruptedException 796 { 797 testCommon(Pages.lateController.name(),null,false,true); 798 } 799 800 /** 801 * 802 * @testableAssertId variable_resolution 803 */ 804 @Test 805 public void variableResolutionSlot() throws InterruptedException 806 { 807 testCommon(Pages.variableResolution.name(),null,false,true); 808 } 809 810 /** 811 * 812 * @testableAssertId external_script_code 813 */ 814 @Test 815 public void externalScriptCodeSlot() throws InterruptedException { 816 testCommon(Pages.externalScript.name(),null,false,true); 817 final LabeledDock ld = new LabeledDock(new SceneDock(getScene()).asParent(), "button_external_script"); 818 ld.mouse().click(); 819 ld.wrap().waitState(new State<String>() { 820 821 @Override 822 public String reached() { 823 return ld.getText(); 824 } 825 }, "Clicked"); 826 } 827 828 //Util 829 @BeforeClass 830 public static void runUI() { 831 staticPropertyLoadApp.main(null); 832 if(isEmbedded()) { 833 Environment.getEnvironment().setTimeout(Wrap.WAIT_STATE_TIMEOUT, 5000); 834 } 835 } 836 837 @Override 838 protected String getName() { 839 return "staticPropertyLoad"; 840 } 841 842 }