1 /*
   2  * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package javafx.scene.control;
  27 
  28 import com.sun.javafx.scene.control.infrastructure.ControlTestUtils;
  29 import static org.junit.Assert.assertEquals;
  30 import static org.junit.Assert.assertFalse;
  31 import static org.junit.Assert.assertNotNull;
  32 import static org.junit.Assert.assertNull;
  33 import static org.junit.Assert.assertSame;
  34 import static org.junit.Assert.assertTrue;
  35 import static org.junit.Assert.fail;
  36 
  37 import java.util.*;
  38 
  39 import com.sun.javafx.scene.control.infrastructure.StageLoader;
  40 import javafx.collections.FXCollections;
  41 import javafx.collections.ListChangeListener;
  42 import javafx.collections.ObservableList;
  43 import javafx.scene.control.ListView.ListViewFocusModel;
  44 import javafx.scene.control.TableView.TableViewFocusModel;
  45 import javafx.scene.control.TableView.TableViewSelectionModel;
  46 import javafx.scene.control.TreeTableView.TreeTableViewFocusModel;
  47 import javafx.scene.control.TreeTableView.TreeTableViewSelectionModel;
  48 import javafx.scene.control.TreeView.TreeViewFocusModel;
  49 
  50 import org.junit.After;
  51 import org.junit.AfterClass;
  52 import org.junit.Assert;
  53 import org.junit.Before;
  54 import org.junit.Ignore;
  55 import org.junit.Test;
  56 import org.junit.runner.RunWith;
  57 import org.junit.runners.Parameterized;
  58 import org.junit.runners.Parameterized.Parameters;
  59 
  60 import com.sun.javafx.scene.control.infrastructure.VirtualFlowTestUtils;
  61 
  62 /**
  63  * Unit tests for the SelectionModel abstract class used by ListView
  64  * and TreeView. This unit test attempts to test all known implementations, and
  65  * as such contains some conditional logic to handle the various controls as
  66  * simply as possible.
  67  */
  68 @RunWith(Parameterized.class)
  69 public class MultipleSelectionModelImplTest {
  70 
  71     private MultipleSelectionModel model;
  72     private FocusModel focusModel;
  73 
  74     private Class<? extends MultipleSelectionModel> modelClass;
  75     private Control currentControl;
  76 
  77     // ListView
  78     private ListView<String> listView;
  79 
  80     // ListView model data
  81     private static ObservableList<String> defaultData = FXCollections.<String>observableArrayList();
  82     private static ObservableList<String> data = FXCollections.<String>observableArrayList();
  83     private static final String ROW_1_VALUE = "Row 1";
  84     private static final String ROW_2_VALUE = "Row 2";
  85     private static final String ROW_3_VALUE = "Long Row 3";
  86     private static final String ROW_5_VALUE = "Row 5";
  87     private static final String ROW_20_VALUE = "Row 20";
  88 
  89     // TreeView
  90     private TreeView treeView;
  91     private TreeItem<String> root;
  92     private TreeItem<String> ROW_2_TREE_VALUE;
  93     private TreeItem<String> ROW_5_TREE_VALUE;
  94     
  95     // TreeTableView
  96     private TreeTableView treeTableView;
  97 
  98     // TableView
  99     private TableView tableView;
 100 
 101     @Parameters public static Collection implementations() {
 102         return Arrays.asList(new Object[][] {
 103             { ListView.ListViewBitSetSelectionModel.class },
 104             { TreeView.TreeViewBitSetSelectionModel.class },
 105             { TableView.TableViewArrayListSelectionModel.class },
 106             { TreeTableView.TreeTableViewArrayListSelectionModel.class },
 107         });
 108     }
 109 
 110     public MultipleSelectionModelImplTest(Class<? extends MultipleSelectionModel> modelClass) {
 111         this.modelClass = modelClass;
 112     }
 113 
 114     @AfterClass public static void tearDownClass() throws Exception {    }
 115 
 116     @Before public void setUp() {
 117         // ListView init
 118         defaultData.setAll(ROW_1_VALUE, ROW_2_VALUE, ROW_3_VALUE, "Row 4", ROW_5_VALUE, "Row 6",
 119                 "Row 7", "Row 8", "Row 9", "Row 10", "Row 11", "Row 12", "Row 13",
 120                 "Row 14", "Row 15", "Row 16", "Row 17", "Row 18", "Row 19", ROW_20_VALUE);
 121 
 122         data.setAll(defaultData);
 123         listView = new ListView<>(data);
 124         // --- ListView init
 125 
 126         // TreeView init
 127         root = new TreeItem<>(ROW_1_VALUE);
 128         root.setExpanded(true);
 129         for (int i = 1; i < data.size(); i++) {
 130             root.getChildren().add(new TreeItem<>(data.get(i)));
 131         }
 132         ROW_2_TREE_VALUE = root.getChildren().get(0);
 133         ROW_5_TREE_VALUE = root.getChildren().get(3);
 134 
 135         treeView = new TreeView(root);
 136         // --- TreeView init
 137 
 138         // TreeTableView init
 139         treeTableView = new TreeTableView(root);
 140         // --- TreeView init
 141 
 142         // TableView init
 143         tableView = new TableView();
 144         tableView.setItems(data);
 145         // --- TableView init
 146 
 147         try {
 148             // reset the data model
 149             data = FXCollections.<String>observableArrayList();
 150             data.setAll(defaultData);
 151 
 152             // we create a new SelectionModel per test to ensure it is always back
 153             // at the default settings
 154             if (modelClass.equals(ListView.ListViewBitSetSelectionModel.class)) {
 155                 // recreate the selection model
 156                 model = modelClass.getConstructor(ListView.class).newInstance(listView);
 157                 listView.setSelectionModel((MultipleSelectionModel<String>)model);
 158 
 159                 // create a new focus model
 160                 focusModel = new ListViewFocusModel(listView);
 161                 listView.setFocusModel(focusModel);
 162                 currentControl = listView;
 163             } else if (modelClass.equals(TreeView.TreeViewBitSetSelectionModel.class)) {
 164                 model = modelClass.getConstructor(TreeView.class).newInstance(treeView);
 165                 treeView.setSelectionModel((MultipleSelectionModel<String>)model);
 166                 focusModel = treeView.getFocusModel();
 167 
 168                 // create a new focus model
 169                 focusModel = new TreeViewFocusModel(treeView);
 170                 treeView.setFocusModel(focusModel);
 171                 currentControl = treeView;
 172             } else if (TableViewSelectionModel.class.isAssignableFrom(modelClass)) {
 173                 // recreate the selection model
 174                 model = modelClass.getConstructor(TableView.class).newInstance(tableView);
 175                 tableView.setSelectionModel((TableViewSelectionModel) model);
 176 
 177                 // create a new focus model
 178                 focusModel = new TableViewFocusModel(tableView);
 179                 tableView.setFocusModel((TableViewFocusModel) focusModel);
 180                 currentControl = tableView;
 181             } else if (TreeTableViewSelectionModel.class.isAssignableFrom(modelClass)) {
 182                 // recreate the selection model
 183                 model = modelClass.getConstructor(TreeTableView.class).newInstance(treeTableView);
 184                 treeTableView.setSelectionModel((TreeTableViewSelectionModel) model);
 185 
 186                 // create a new focus model
 187                 focusModel = new TreeTableViewFocusModel(treeTableView);
 188                 treeTableView.setFocusModel((TreeTableViewFocusModel) focusModel);
 189                 currentControl = treeTableView;
 190             }
 191 
 192             // ensure the selection mode is set to multiple
 193             model.setSelectionMode(SelectionMode.MULTIPLE);
 194         } catch (Exception ex) {
 195             ex.printStackTrace();
 196         }
 197     }
 198 
 199     @After public void tearDown() {
 200         model = null;
 201     }
 202 
 203     private Object getValue(Object item) {
 204         if (item instanceof TreeItem) {
 205             return ((TreeItem)item).getValue();
 206         }
 207         return item;
 208     }
 209 
 210     private String indices(MultipleSelectionModel sm) {
 211         return "Selected Indices: " + sm.getSelectedIndices();
 212     }
 213 
 214     private String items(MultipleSelectionModel sm) {
 215         return "Selected Items: " + sm.getSelectedItems();
 216     }
 217 
 218     private MultipleSelectionModel msModel() {
 219         return model;
 220     }
 221     
 222     private boolean isTree() {
 223         return model instanceof TreeView.TreeViewBitSetSelectionModel ||
 224                model instanceof TreeTableView.TreeTableViewArrayListSelectionModel;
 225     }
 226 
 227     private void ensureInEmptyState() {
 228         assertEquals(-1, model.getSelectedIndex());
 229         assertNull(model.getSelectedItem());
 230 
 231         if (focusModel != null) {
 232             assertEquals(-1, focusModel.getFocusedIndex());
 233             assertNull(focusModel.getFocusedItem());
 234         }
 235 
 236         assertNotNull(msModel().getSelectedIndices());
 237         assertNotNull(msModel().getSelectedItems());
 238         assertEquals(0, msModel().getSelectedIndices().size());
 239         assertEquals(0, msModel().getSelectedItems().size());
 240     }
 241 
 242     @Test public void ensureInDefaultState() {
 243         assertEquals(-1, model.getSelectedIndex());
 244         assertNull(model.getSelectedItem());
 245 
 246         if (focusModel != null) {
 247             assertEquals(0, focusModel.getFocusedIndex());
 248             assertNotNull(focusModel.getFocusedItem());
 249         }
 250 
 251         assertNotNull(msModel().getSelectedIndices());
 252         assertNotNull(msModel().getSelectedItems());
 253         assertEquals(0, msModel().getSelectedIndices().size());
 254         assertEquals(0, msModel().getSelectedItems().size());
 255     }
 256 
 257     @Test public void selectValidIndex() {
 258         int index = 4;
 259         model.clearSelection();
 260         model.select(index);
 261 
 262         assertEquals(index, model.getSelectedIndex());
 263         assertNotNull(model.getSelectedItem());
 264         assertEquals(ROW_5_VALUE, getValue(model.getSelectedItem()));
 265 
 266         if (focusModel != null) {
 267             assertEquals(index, focusModel.getFocusedIndex());
 268             assertNotNull(focusModel.getFocusedItem());
 269             assertEquals(ROW_5_VALUE, getValue(focusModel.getFocusedItem()));
 270         }
 271 
 272         assertNotNull(msModel().getSelectedIndices());
 273         assertNotNull(msModel().getSelectedItems());
 274         assertEquals(1, msModel().getSelectedIndices().size());
 275         assertEquals(index, msModel().getSelectedIndices().get(0));
 276         assertEquals(1, msModel().getSelectedItems().size());
 277         assertEquals(ROW_5_VALUE, getValue(msModel().getSelectedItems().get(0)));
 278     }
 279 
 280     @Test public void testSelectAllWithSingleSelection() {
 281         msModel().setSelectionMode(SelectionMode.SINGLE);
 282         msModel().selectAll();
 283         ensureInDefaultState();
 284     }
 285 
 286     @Test public void testSelectAllWithMultipleSelection() {
 287         msModel().clearSelection();
 288         msModel().selectAll();
 289 
 290         assertEquals(19, model.getSelectedIndex());
 291         assertNotNull(model.getSelectedItem());
 292         assertEquals(ROW_20_VALUE, getValue(model.getSelectedItem()));
 293         assertEquals(19, focusModel.getFocusedIndex());
 294         assertNotNull(focusModel.getFocusedItem());
 295         assertEquals(ROW_20_VALUE, getValue(focusModel.getFocusedItem()));
 296         assertNotNull(msModel().getSelectedIndices());
 297         assertNotNull(msModel().getSelectedItems());
 298         assertEquals(20, msModel().getSelectedIndices().size());
 299         assertEquals(20, msModel().getSelectedItems().size());
 300     }
 301 
 302     @Test public void clearAllSelection() {
 303         msModel().selectAll();
 304         model.clearSelection();
 305         ensureInEmptyState();
 306     }
 307 
 308     @Test public void clearPartialSelectionWithSingleSelection() {
 309         model.clearSelection();
 310         assertFalse(model.isSelected(5));
 311         model.select(5);
 312         assertTrue(model.isSelected(5));
 313         model.clearSelection(5);
 314         assertFalse(model.isSelected(5));
 315 
 316         assertEquals(0, msModel().getSelectedIndices().size());
 317         assertEquals(0, msModel().getSelectedItems().size());
 318     }
 319 
 320     @Test public void clearPartialSelectionWithMultipleSelection() {
 321         msModel().setSelectionMode(SelectionMode.MULTIPLE);
 322         msModel().selectAll();
 323         model.clearSelection(5);
 324 
 325         assertTrue(model.isSelected(4));
 326         assertFalse(model.isSelected(5));
 327         assertTrue(model.isSelected(6));
 328     }
 329 
 330     @Test public void testSelectedIndicesObservableListIsEmpty() {
 331         assertTrue(msModel().getSelectedIndices().isEmpty());
 332     }
 333 
 334     @Test public void testSelectedIndicesIteratorIsNotNull() {
 335         assertNotNull(msModel().getSelectedIndices().iterator());
 336     }
 337 
 338     @Test public void testSelectedIndicesIteratorHasNoNext() {
 339         assertFalse(msModel().getSelectedIndices().iterator().hasNext());
 340     }
 341 
 342     @Test public void testSelectedIndicesIteratorWorksWithSingleSelection() {
 343         msModel().clearSelection();
 344         model.select(5);
 345 
 346         Iterator<Integer> it = msModel().getSelectedIndices().iterator();
 347         assertEquals(5, (int) it.next());
 348         assertFalse(it.hasNext());
 349     }
 350 
 351     @Test public void testSelectedIndicesIteratorWorksWithMultipleSelection() {
 352         msModel().clearSelection();
 353         msModel().setSelectionMode(SelectionMode.MULTIPLE);
 354         msModel().selectIndices(1, 2, 5);
 355 
 356         Iterator<Integer> it = msModel().getSelectedIndices().iterator();
 357         assertEquals(indices(msModel()), 1, (int) it.next());
 358         assertEquals(indices(msModel()), 2, (int) it.next());
 359         assertEquals(indices(msModel()), 5, (int) it.next());
 360         assertFalse(it.hasNext());
 361     }
 362 
 363     @Test public void testSelectedIndicesContains() {
 364         model.select(5);
 365         assertTrue(msModel().getSelectedIndices().contains(5));
 366     }
 367 
 368     @Test public void testSelectedItemsObservableListIsEmpty() {
 369         assertTrue(msModel().getSelectedItems().isEmpty());
 370     }
 371 
 372     @Test public void testSelectedItemsIndexOf() {
 373         msModel().clearSelection();
 374         if (isTree()) {
 375             model.select(ROW_2_TREE_VALUE);
 376             assertEquals(1, msModel().getSelectedItems().size());
 377             assertEquals(0, msModel().getSelectedItems().indexOf(ROW_2_TREE_VALUE));
 378         } else {
 379             model.select(ROW_2_VALUE);
 380             assertEquals(1, msModel().getSelectedItems().size());
 381             assertEquals(0, msModel().getSelectedItems().indexOf(ROW_2_VALUE));
 382         }
 383     }
 384 
 385     @Test public void testSelectedItemsIteratorIsNotNull() {
 386         assertNotNull(msModel().getSelectedIndices().iterator());
 387     }
 388 
 389     @Test public void testSelectedItemsLastIndexOf() {
 390         msModel().clearSelection();
 391         if (isTree()) {
 392             model.select(ROW_2_TREE_VALUE);
 393             assertEquals(1, msModel().getSelectedItems().size());
 394             assertEquals(0, msModel().getSelectedItems().lastIndexOf(ROW_2_TREE_VALUE));
 395         } else {
 396             model.select(ROW_2_VALUE);
 397             assertEquals(1, msModel().getSelectedItems().size());
 398             assertEquals(0, msModel().getSelectedItems().lastIndexOf(ROW_2_VALUE));
 399         }
 400     }
 401 
 402     @Test public void testSelectedItemsContains() {
 403         model.select(5);
 404         assertTrue(msModel().getSelectedIndices().contains(5));
 405     }
 406 
 407     @Test public void testSingleSelectionMode() {
 408         msModel().clearSelection();
 409         msModel().setSelectionMode(SelectionMode.SINGLE);
 410         assertTrue(model.isEmpty());
 411 
 412         model.select(5);
 413         assertTrue(model.isSelected(5));
 414 
 415         model.select(10);
 416         assertTrue(model.isSelected(10));
 417         assertFalse(model.isSelected(5));
 418 
 419         assertEquals(1, msModel().getSelectedIndices().size());
 420         assertEquals(1, msModel().getSelectedItems().size());
 421     }
 422 
 423     @Test public void testMultipleSelectionMode() {
 424         msModel().clearSelection();
 425         msModel().setSelectionMode(SelectionMode.MULTIPLE);
 426         assertTrue(model.isEmpty());
 427 
 428         model.select(5);
 429         assertTrue(model.isSelected(5));
 430 
 431         model.select(10);
 432         assertTrue(model.isSelected(10));
 433         assertTrue(model.isSelected(5));
 434         assertEquals(2, msModel().getSelectedIndices().size());
 435         assertEquals(2, msModel().getSelectedItems().size());
 436     }
 437 
 438     @Test public void testChangeSelectionMode() {
 439         msModel().clearSelection();
 440         msModel().setSelectionMode(SelectionMode.MULTIPLE);
 441         msModel().selectIndices(5, 10, 15);
 442         assertEquals(indices(msModel()), 3, msModel().getSelectedIndices().size());
 443         assertEquals(3, msModel().getSelectedItems().size());
 444         assertTrue(model.isSelected(5));
 445         assertTrue(model.isSelected(10));
 446         assertTrue(model.isSelected(15));
 447 
 448         msModel().setSelectionMode(SelectionMode.SINGLE);
 449         assertEquals(1, msModel().getSelectedIndices().size());
 450         assertEquals(1, msModel().getSelectedItems().size());
 451         assertFalse(indices(msModel()), model.isSelected(5));
 452         assertFalse(indices(msModel()), model.isSelected(10));
 453         assertTrue(indices(msModel()), model.isSelected(15));
 454     }
 455 
 456 //    @Test public void testSelectNullObject() {
 457 //        model.select(null);
 458 //    }
 459 
 460     @Test public void testSelectRange() {
 461         // should select all indices starting at 5, and finishing just before
 462         // the 10th item
 463         msModel().clearSelection();
 464         msModel().setSelectionMode(SelectionMode.MULTIPLE);
 465         msModel().selectRange(5, 10);
 466         assertEquals(indices(msModel()), 5, msModel().getSelectedIndices().size());
 467         assertEquals(5, msModel().getSelectedItems().size());
 468         assertFalse(model.isSelected(4));
 469         assertTrue(model.isSelected(5));
 470         assertTrue(model.isSelected(6));
 471         assertTrue(model.isSelected(7));
 472         assertTrue(model.isSelected(8));
 473         assertTrue(model.isSelected(9));
 474         assertFalse(model.isSelected(10));
 475     }
 476 
 477     @Test public void testDeselectionFromASelectionRange() {
 478         msModel().setSelectionMode(SelectionMode.MULTIPLE);
 479         msModel().selectRange(2, 10);
 480         model.clearSelection(5);
 481         assertTrue(indices(msModel()), model.isSelected(4));
 482         assertFalse(indices(msModel()), model.isSelected(5));
 483         assertTrue(indices(msModel()), model.isSelected(6));
 484     }
 485 
 486     @Test public void testAccurateItemSelection() {
 487         msModel().clearSelection();
 488         msModel().setSelectionMode(SelectionMode.MULTIPLE);
 489         msModel().selectRange(2, 5);
 490         ObservableList selectedItems = msModel().getSelectedItems();
 491         assertEquals(3, selectedItems.size());
 492 
 493         if (isTree()) {
 494             assertFalse(selectedItems.contains(root.getChildren().get(0)));
 495             assertTrue(selectedItems.contains(root.getChildren().get(1)));
 496             assertTrue(selectedItems.contains(root.getChildren().get(2)));
 497             assertTrue(selectedItems.contains(root.getChildren().get(3)));
 498             assertFalse(selectedItems.contains(root.getChildren().get(4)));
 499         } else {
 500             assertFalse(selectedItems.contains(data.get(1)));
 501             assertTrue(selectedItems.contains(data.get(2)));
 502             assertTrue(selectedItems.contains(data.get(3)));
 503             assertTrue(selectedItems.contains(data.get(4)));
 504             assertFalse(selectedItems.contains(data.get(5)));
 505         }
 506     }
 507 
 508     @Test public void ensureSelectedIndexAndItemIsAlwaysTheLastSelectionWithSelect() {
 509         msModel().setSelectionMode(SelectionMode.MULTIPLE);
 510         model.select(3);
 511         assertEquals(3, model.getSelectedIndex());
 512         if (isTree()) {
 513             assertEquals(root.getChildren().get(2), model.getSelectedItem());
 514         } else {
 515             assertEquals(data.get(3), model.getSelectedItem());
 516         }
 517 
 518         model.select(1);
 519         assertEquals(1, model.getSelectedIndex());
 520         if (isTree()) {
 521             assertEquals(root.getChildren().get(0), model.getSelectedItem());
 522         } else {
 523             assertEquals(data.get(1), model.getSelectedItem());
 524         }
 525 
 526         model.select(5);
 527         assertEquals(5, model.getSelectedIndex());
 528         if (isTree()) {
 529             assertEquals(root.getChildren().get(4), model.getSelectedItem());
 530         } else {
 531             assertEquals(data.get(5), model.getSelectedItem());
 532         }
 533 
 534         model.select(1);
 535         assertEquals(1, model.getSelectedIndex());
 536         if (isTree()) {
 537             assertEquals(root.getChildren().get(0), model.getSelectedItem());
 538         } else {
 539             assertEquals(data.get(1), model.getSelectedItem());
 540         }
 541     }
 542 
 543     @Test public void ensureSelectedIndexAndItemIsAlwaysTheLastSelectionWithMultipleSelect() {
 544         msModel().setSelectionMode(SelectionMode.MULTIPLE);
 545         msModel().selectIndices(3,4,5);
 546         assertEquals(5, model.getSelectedIndex());
 547 
 548         if (isTree()) {
 549             assertEquals(root.getChildren().get(4), model.getSelectedItem());
 550         } else {
 551             assertEquals(data.get(5), model.getSelectedItem());
 552         }
 553 
 554         model.select(1);
 555         assertEquals(1, model.getSelectedIndex());
 556 
 557         if (isTree()) {
 558             assertEquals(root.getChildren().get(0), model.getSelectedItem());
 559         } else {
 560             assertEquals(data.get(1), model.getSelectedItem());
 561         }
 562 
 563         msModel().selectIndices(8,7,6);
 564         assertEquals(6, model.getSelectedIndex());
 565         if (isTree()) {
 566             assertEquals(root.getChildren().get(5), model.getSelectedItem());
 567         } else {
 568             assertEquals(data.get(6), model.getSelectedItem());
 569         }
 570     }
 571 
 572     @Test public void ensureSelectedIndexAndItemIsAlwaysTheLastSelectionWithSelectRange() {
 573         msModel().setSelectionMode(SelectionMode.MULTIPLE);
 574         msModel().selectRange(3,10);
 575         assertEquals(9, model.getSelectedIndex());
 576         if (isTree()) {
 577             assertEquals(root.getChildren().get(8), model.getSelectedItem());
 578         } else {
 579             assertEquals(data.get(9), model.getSelectedItem());
 580         }
 581 
 582         model.select(1);
 583         assertEquals(1, model.getSelectedIndex());
 584         if (isTree()) {
 585             assertEquals(root.getChildren().get(0), model.getSelectedItem());
 586         } else {
 587             assertEquals(data.get(1), model.getSelectedItem());
 588         }
 589 
 590         msModel().selectRange(6, 8);
 591         assertEquals(7, model.getSelectedIndex());
 592         if (isTree()) {
 593             assertEquals(root.getChildren().get(6), model.getSelectedItem());
 594         } else {
 595             assertEquals(data.get(7), model.getSelectedItem());
 596         }
 597     }
 598 
 599     @Test public void testMultipleSelectionWithEmptyArray() {
 600         msModel().clearSelection();
 601         msModel().setSelectionMode(SelectionMode.MULTIPLE);
 602         msModel().selectIndices(3,new int[] { });
 603         assertEquals(3, model.getSelectedIndex());
 604         assertEquals(1, msModel().getSelectedIndices().size());
 605         assertEquals(1, msModel().getSelectedItems().size());
 606 
 607         if (isTree()) {
 608             assertEquals(root.getChildren().get(2), model.getSelectedItem());
 609         } else {
 610             assertEquals(data.get(3), model.getSelectedItem());
 611         }
 612     }
 613 
 614     @Test public void selectOnlyValidIndicesInSingleSelection() {
 615         msModel().setSelectionMode(SelectionMode.SINGLE);
 616         msModel().selectIndices(750397, 3, 709709375, 4, 8597998, 47929);
 617         assertEquals(4, model.getSelectedIndex());
 618         assertFalse(model.isSelected(3));
 619         assertTrue(model.isSelected(4));
 620         assertEquals(1, msModel().getSelectedIndices().size());
 621         assertEquals(1, msModel().getSelectedItems().size());
 622 
 623         if (isTree()) {
 624             assertEquals(root.getChildren().get(3), model.getSelectedItem());
 625         } else {
 626             assertEquals(data.get(4), model.getSelectedItem());
 627         }
 628     }
 629 
 630     @Test public void selectOnlyValidIndicesInMultipleSelection() {
 631         msModel().setSelectionMode(SelectionMode.MULTIPLE);
 632         model.clearSelection();
 633         msModel().selectIndices(750397, 3, 709709375, 4, 8597998, 47929);
 634         assertEquals(4, model.getSelectedIndex());
 635         assertTrue(model.isSelected(3));
 636         assertTrue(model.isSelected(4));
 637         assertEquals(2, msModel().getSelectedIndices().size());
 638         assertEquals(2, msModel().getSelectedItems().size());
 639 
 640         if (isTree()) {
 641             assertEquals(root.getChildren().get(3), model.getSelectedItem());
 642         } else {
 643             assertEquals(data.get(4), model.getSelectedItem());
 644         }
 645     }
 646 
 647     @Test public void testNullArrayInMultipleSelection() {
 648         msModel().clearSelection();
 649         msModel().setSelectionMode(SelectionMode.MULTIPLE);
 650         msModel().selectIndices(-20, null);
 651         assertEquals(-1, model.getSelectedIndex());
 652         assertNull(model.getSelectedItem());
 653         assertEquals(indices(msModel()), 0, msModel().getSelectedIndices().size());
 654         assertEquals(items(msModel()), 0, msModel().getSelectedItems().size());
 655     }
 656 
 657     @Test public void testMultipleSelectionWithInvalidIndices() {
 658         msModel().clearSelection();
 659         msModel().setSelectionMode(SelectionMode.MULTIPLE);
 660         msModel().selectIndices(-20, 23505, 78125);
 661         assertEquals(-1, model.getSelectedIndex());
 662         assertNull(model.getSelectedItem());
 663         assertEquals(indices(msModel()), 0, msModel().getSelectedIndices().size());
 664         assertEquals(items(msModel()), 0, msModel().getSelectedItems().size());
 665     }
 666 
 667     @Test public void testInvalidSelection() {
 668         msModel().clearSelection();
 669         msModel().setSelectionMode(SelectionMode.SINGLE);
 670         msModel().selectIndices(-20, null);
 671         assertEquals(-1, model.getSelectedIndex());
 672         assertNull(model.getSelectedItem());
 673         assertEquals(indices(msModel()), 0, msModel().getSelectedIndices().size());
 674         assertEquals(items(msModel()), 0, msModel().getSelectedItems().size());
 675     }
 676 
 677     @Test public void ensureSwappedSelectRangeWorks() {
 678         // first test a valid range - there should be 6 selected items
 679         model.clearSelection();
 680         model.setSelectionMode(SelectionMode.MULTIPLE);
 681         model.selectRange(3, 10);
 682         assertEquals(indices(model), 7, model.getSelectedIndices().size());
 683         assertEquals(9, model.getSelectedIndex());
 684         if (isTree()) {
 685             assertEquals(root.getChildren().get(8), model.getSelectedItem());
 686         } else {
 687             assertEquals(data.get(9), model.getSelectedItem());
 688         }
 689        
 690         model.clearSelection();
 691         
 692         // we should select the range from 10 - 4 inclusive
 693         model.selectRange(10, 3);
 694         assertEquals(7, model.getSelectedIndices().size());
 695         assertEquals(indices(model), 4, model.getSelectedIndex());
 696         if (isTree()) {
 697             assertEquals(root.getChildren().get(3), model.getSelectedItem());
 698         } else {
 699             assertEquals(data.get(4), model.getSelectedItem());
 700         }
 701     }
 702 
 703     @Test public void testInvalidSelectRange() {
 704         msModel().clearSelection();
 705         msModel().setSelectionMode(SelectionMode.MULTIPLE);
 706         msModel().selectRange(200, 220);
 707         assertEquals(-1, model.getSelectedIndex());
 708         assertEquals(null, model.getSelectedItem());
 709         assertEquals(indices(msModel()), 0, msModel().getSelectedIndices().size());
 710         assertEquals(items(msModel()), 0, msModel().getSelectedItems().size());
 711     }
 712 
 713     @Test public void testEmptySelectRange() {
 714         msModel().clearSelection();
 715         msModel().setSelectionMode(SelectionMode.MULTIPLE);
 716         msModel().selectRange(10, 10);
 717         assertEquals(-1, model.getSelectedIndex());
 718         assertEquals(null, model.getSelectedItem());
 719         assertEquals(indices(msModel()), 0, msModel().getSelectedIndices().size());
 720         assertEquals(items(msModel()), 0, msModel().getSelectedItems().size());
 721     }
 722 
 723     @Test public void testNegativeSelectRange() {
 724         msModel().clearSelection();
 725         msModel().setSelectionMode(SelectionMode.MULTIPLE);
 726         msModel().selectRange(-10, -1);
 727         assertEquals(-1, model.getSelectedIndex());
 728         assertEquals(null, model.getSelectedItem());
 729         assertEquals(indices(msModel()), 0, msModel().getSelectedIndices().size());
 730         assertEquals(items(msModel()), 0, msModel().getSelectedItems().size());
 731     }
 732 
 733     @Test(expected=IllegalArgumentException.class)
 734     public void testNullListViewInSelectionModel() {
 735         new ListView.ListViewBitSetSelectionModel(null);
 736     }
 737 
 738     @Test(expected=IllegalArgumentException.class)
 739     public void testNullTreeViewInSelectionModel() {
 740         new TreeView.TreeViewBitSetSelectionModel(null);
 741     }
 742 
 743     @Test public void selectAllInEmptySingleSelectionMode() {
 744         model.clearSelection();
 745         msModel().setSelectionMode(SelectionMode.SINGLE);
 746         assertTrue(model.isEmpty());
 747         msModel().selectAll();
 748         assertTrue(model.isEmpty());
 749     }
 750 
 751     @Test public void selectAllInSingleSelectionModeWithSelectedRow() {
 752         msModel().setSelectionMode(SelectionMode.SINGLE);
 753         model.clearSelection();
 754         assertTrue(model.isEmpty());
 755         model.select(3);
 756         msModel().selectAll();
 757         assertTrue(model.isSelected(3));
 758         assertEquals(1, msModel().getSelectedIndices().size());
 759     }
 760 
 761     @Test public void selectionModePropertyHasReferenceToBean() {
 762         assertSame(model, model.selectionModeProperty().getBean());
 763     }
 764 
 765     @Test public void selectionModePropertyHasName() {
 766         assertSame("selectionMode", model.selectionModeProperty().getName());
 767     }
 768 
 769     @Ignore("Not yet implemented in TreeView and TableView")
 770     @Test public void testSelectionChangesWhenItemIsInsertedAtStartOfModel() {
 771         /* Select the fourth item, and insert a new item at the start of the
 772          * data model. The end result should be that the fourth item should NOT
 773          * be selected, and the fifth item SHOULD be selected.
 774          */
 775         model.select(3);
 776         assertTrue(model.isSelected(3));
 777         data.add(0, "Inserted String");
 778         assertFalse(model.isSelected(3));
 779         assertTrue(model.isSelected(4));
 780     }
 781     
 782     private int rt_28615_row_1_hit_count = 0;
 783     private int rt_28615_row_2_hit_count = 0;
 784     @Test public void test_rt_28615() {
 785         model.clearSelection();
 786         msModel().setSelectionMode(SelectionMode.MULTIPLE);
 787 
 788         msModel().getSelectedItems().addListener((ListChangeListener.Change change) -> {
 789             while (change.next()) {
 790                 if (change.wasAdded()) {
 791                     for (Object item : change.getAddedSubList()) {
 792                         if (isTree()) {
 793                             if (root.equals(item)) {
 794                                 rt_28615_row_1_hit_count++;
 795                             } else if (ROW_2_TREE_VALUE.equals(item)) {
 796                                 rt_28615_row_2_hit_count++;
 797                             }
 798                         } else {
 799                             if (ROW_1_VALUE.equals(item)) {
 800                                 rt_28615_row_1_hit_count++;
 801                             } else if (ROW_2_VALUE.equals(item)) {
 802                                 rt_28615_row_2_hit_count++;
 803                             }
 804                         }
 805                     }
 806                 }
 807             }
 808         });
 809 
 810         assertEquals(0, rt_28615_row_1_hit_count);
 811         assertEquals(0, rt_28615_row_2_hit_count);
 812         
 813         msModel().select(0);
 814         assertEquals(1, rt_28615_row_1_hit_count);
 815         assertEquals(0, rt_28615_row_2_hit_count);
 816         
 817         msModel().select(1);
 818         assertEquals(1, rt_28615_row_1_hit_count);
 819         assertEquals(1, rt_28615_row_2_hit_count);
 820     }
 821     
 822     private int rt_29860_size_count = 0;
 823     @Test public void test_rt_29860_add() {
 824         model.clearSelection();
 825         msModel().setSelectionMode(SelectionMode.MULTIPLE);
 826 
 827         msModel().getSelectedIndices().addListener((ListChangeListener.Change change) -> {
 828             while (change.next()) {
 829                 if (change.wasAdded()) {
 830                     rt_29860_size_count += change.getAddedSize();
 831                 }
 832             }
 833         });
 834 
 835         assertEquals(0, rt_29860_size_count);
 836         
 837         // 0,1,2,3 are all selected. The bug is not that the msModel().getSelectedIndices()
 838         // list is wrong (it isn't - it's correct). The bug is that the addedSize
 839         // reported in the callback above is incorrect.
 840         msModel().selectIndices(0, 1, 2, 3);
 841         assertEquals(msModel().getSelectedIndices().toString(), 4, rt_29860_size_count);   
 842         rt_29860_size_count = 0;
 843         
 844         msModel().selectIndices(0,1,2,3,4);
 845         assertEquals(msModel().getSelectedIndices().toString(), 1, rt_29860_size_count);   // only 4 was selected
 846         rt_29860_size_count = 0;
 847         
 848         msModel().selectIndices(6,7,8);
 849         assertEquals(3, rt_29860_size_count);   // 6,7,8 was selected
 850     }
 851     
 852     @Test public void test_rt_29821() {
 853         msModel().setSelectionMode(SelectionMode.MULTIPLE);
 854         
 855         IndexedCell cell_3 = VirtualFlowTestUtils.getCell(currentControl, 3);
 856         assertNotNull(cell_3);
 857         assertFalse(cell_3.isSelected());
 858 
 859         msModel().clearSelection();
 860         msModel().select(3);
 861         assertTrue(cell_3.isSelected());
 862         assertEquals(1, msModel().getSelectedIndices().size());
 863 
 864         // in multiple selection passing in select(null) is a no-op. In single
 865         // selection (tested elsewhere), this would result in a clearSelection() 
 866         // call
 867         msModel().select(null);
 868         assertTrue(msModel().isSelected(3));
 869         assertTrue(cell_3.isSelected());
 870     }
 871 
 872     private int rt_32411_add_count = 0;
 873     private int rt_32411_remove_count = 0;
 874     @Test public void test_rt_32411_selectedItems() {
 875         model.clearSelection();
 876 
 877         model.getSelectedItems().addListener((ListChangeListener.Change change) -> {
 878             while (change.next()) {
 879                 rt_32411_remove_count += change.getRemovedSize();
 880                 rt_32411_add_count += change.getAddedSize();
 881             }
 882         });
 883 
 884         // reset fields
 885         rt_32411_add_count = 0;
 886         rt_32411_remove_count = 0;
 887 
 888         // select a row - no problems here
 889         model.select(2);
 890         assertEquals(1, rt_32411_add_count);
 891         assertEquals(0, rt_32411_remove_count);
 892 
 893         // clear and select a new row. We should receive a remove event followed
 894         // by an added event - but this bug shows we don't get the remove event.
 895         model.clearAndSelect(4);
 896         assertEquals(2, rt_32411_add_count);
 897         assertEquals(1, rt_32411_remove_count);
 898     }
 899 
 900     @Test public void test_rt_32411_selectedIndices() {
 901         model.clearSelection();
 902 
 903         model.getSelectedIndices().addListener((ListChangeListener.Change change) -> {
 904             while (change.next()) {
 905                 rt_32411_remove_count += change.getRemovedSize();
 906                 rt_32411_add_count += change.getAddedSize();
 907             }
 908         });
 909 
 910         // reset fields
 911         rt_32411_add_count = 0;
 912         rt_32411_remove_count = 0;
 913 
 914         // select a row - no problems here
 915         model.select(2);
 916         assertEquals(1, rt_32411_add_count);
 917         assertEquals(0, rt_32411_remove_count);
 918 
 919         // clear and select a new row. We should receive a remove event followed
 920         // by an added event - but this bug shows we don't get the remove event.
 921         model.clearAndSelect(4);
 922         assertEquals(2, rt_32411_add_count);
 923         assertEquals(1, rt_32411_remove_count);
 924     }
 925 
 926     private int rt32618_count = 0;
 927     @Test public void test_rt32618_multipleSelection() {
 928         model.selectedItemProperty().addListener((ov, t, t1) -> rt32618_count++);
 929 
 930         assertEquals(0, rt32618_count);
 931 
 932         model.select(1);
 933         assertEquals(1, rt32618_count);
 934         assertEquals(ROW_2_VALUE, getValue(model.getSelectedItem()));
 935 
 936         model.clearAndSelect(2);
 937         assertEquals(2, rt32618_count);
 938         assertEquals(ROW_3_VALUE, getValue(model.getSelectedItem()));
 939     }
 940 
 941     @Test public void test_rt33324_selectedIndices() {
 942         // pre-select item 0
 943         model.select(0);
 944 
 945         // install listener
 946         model.getSelectedIndices().addListener((ListChangeListener.Change change) -> {
 947             while (change.next()) {
 948                 assertTrue(change.wasRemoved());
 949                 assertTrue(change.wasAdded());
 950                 assertTrue(change.wasReplaced());
 951 
 952                 assertFalse(change.wasPermutated());
 953             }
 954         });
 955 
 956         // change selection to index 1. This should result in a change event
 957         // being fired where wasAdded() is true, wasRemoved() is true, but most
 958         // importantly, wasReplaced() is true
 959         model.clearAndSelect(1);
 960     }
 961 
 962     @Test public void test_rt33324_selectedItems() {
 963         // pre-select item 0
 964         model.select(0);
 965 
 966         // install listener
 967         model.getSelectedItems().addListener((ListChangeListener.Change change) -> {
 968             while (change.next()) {
 969                 assertTrue(change.wasRemoved());
 970                 assertTrue(change.wasAdded());
 971                 assertTrue(change.wasReplaced());
 972 
 973                 assertFalse(change.wasPermutated());
 974             }
 975         });
 976 
 977         // change selection to index 1. This should result in a change event
 978         // being fired where wasAdded() is true, wasRemoved() is true, but most
 979         // importantly, wasReplaced() is true
 980         model.clearAndSelect(1);
 981     }
 982 
 983     @Test public void test_rt33324_selectedCells() {
 984         if (! (msModel() instanceof TableViewSelectionModel)) {
 985             return;
 986         }
 987 
 988         TableViewSelectionModel tableSM = (TableViewSelectionModel) msModel();
 989 
 990         // pre-select item 0
 991         tableSM.select(0);
 992 
 993         // install listener
 994         tableSM.getSelectedCells().addListener((ListChangeListener.Change change) -> {
 995             while (change.next()) {
 996                 assertTrue(change.wasRemoved());
 997                 assertTrue(change.wasAdded());
 998                 assertTrue(change.wasReplaced());
 999 
1000                 assertFalse(change.wasPermutated());
1001             }
1002         });
1003 
1004         // change selection to index 1. This should result in a change event
1005         // being fired where wasAdded() is true, wasRemoved() is true, but most
1006         // importantly, wasReplaced() is true
1007         tableSM.clearAndSelect(1);
1008     }
1009 
1010     @Test public void test_rt35624_selectedIndices_downwards() {
1011         model.clearSelection();
1012         model.select(2);
1013 
1014         msModel().getSelectedIndices().addListener((ListChangeListener.Change change) -> {
1015             while (change.next()) {
1016                 // we expect two items in the added list: 3 and 4, as index
1017                 // 2 has previously been selected
1018                 Assert.assertEquals(2, change.getAddedSize());
1019                 Assert.assertEquals(FXCollections.observableArrayList(3, 4), change.getAddedSubList());
1020             }
1021 
1022             // In the actual list, we expect three items: 2, 3 and 4
1023             Assert.assertEquals(3, change.getList().size());
1024             Assert.assertEquals(FXCollections.observableArrayList(2, 3, 4), change.getList());
1025         });
1026 
1027         model.selectIndices(2, 3, 4);
1028     }
1029 
1030     @Test public void test_rt35624_selectedIndices_upwards() {
1031         model.clearSelection();
1032         model.select(4);
1033 
1034         msModel().getSelectedIndices().addListener(((ListChangeListener.Change change) -> {
1035             while (change.next()) {
1036                 // we expect two items in the added list: 3 and 2, as index
1037                 // 4 has previously been selected
1038                 assertEquals(2, change.getAddedSize());
1039                 assertEquals(FXCollections.observableArrayList(2, 3), change.getAddedSubList());
1040             }
1041 
1042             // In the actual list, we expect three items: 2, 3 and 4
1043             assertEquals(3, change.getList().size());
1044             assertEquals(FXCollections.observableArrayList(2, 3, 4), change.getList());
1045         }));
1046 
1047         model.selectIndices(4, 3, 2);
1048     }
1049 
1050     @Test public void test_rt35624_selectedItems_downwards() {
1051         model.clearSelection();
1052         model.select(2);
1053 
1054         msModel().getSelectedItems().addListener(((ListChangeListener.Change change) -> {
1055             while (change.next()) {
1056                 // we expect two items in the added list: the items in index
1057                 // 3 and 4, as index 2 has previously been selected
1058                 assertEquals(2, change.getAddedSize());
1059 
1060                 if (isTree()) {
1061                     assertEquals(FXCollections.observableArrayList(
1062                             root.getChildren().get(2),
1063                             root.getChildren().get(3)
1064                     ), change.getAddedSubList());
1065                 } else {
1066                     assertEquals(FXCollections.observableArrayList(
1067                             data.get(3),
1068                             data.get(4)
1069                     ), change.getAddedSubList());
1070                 }
1071             }
1072 
1073             // In the actual list, we expect three items: the values at index
1074             // 2, 3 and 4
1075             assertEquals(3, change.getList().size());
1076 
1077             if (isTree()) {
1078                 assertEquals(FXCollections.observableArrayList(
1079                         root.getChildren().get(1),
1080                         root.getChildren().get(2),
1081                         root.getChildren().get(3)
1082                 ), change.getList());
1083             } else {
1084                 assertEquals(FXCollections.observableArrayList(
1085                         data.get(2),
1086                         data.get(3),
1087                         data.get(4)
1088                 ), change.getList());
1089             }
1090         }));
1091 
1092         model.selectIndices(2, 3, 4);
1093     }
1094 
1095     @Test public void test_rt35624_selectedItems_upwards() {
1096         model.clearSelection();
1097         model.select(4);
1098 
1099         msModel().getSelectedItems().addListener(((ListChangeListener.Change change) -> {
1100             while (change.next()) {
1101                 // we expect two items in the added list: the items in index
1102                 // 2 and 3, as index 4 has previously been selected
1103                 assertEquals(2, change.getAddedSize());
1104 
1105                 if (isTree()) {
1106                     assertEquals(FXCollections.observableArrayList(
1107                             root.getChildren().get(1),
1108                             root.getChildren().get(2)
1109                     ), change.getAddedSubList());
1110                 } else {
1111                     assertEquals(FXCollections.observableArrayList(
1112                             data.get(2),
1113                             data.get(3)
1114                     ), change.getAddedSubList());
1115                 }
1116             }
1117 
1118             // In the actual list, we expect three items: the values at index
1119             // 2, 3 and 4
1120             assertEquals(3, change.getList().size());
1121 
1122             if (isTree()) {
1123                 assertEquals(FXCollections.observableArrayList(
1124                         root.getChildren().get(1),
1125                         root.getChildren().get(2),
1126                         root.getChildren().get(3)
1127                 ), change.getList());
1128             } else {
1129                 assertEquals(FXCollections.observableArrayList(
1130                         data.get(2),
1131                         data.get(3),
1132                         data.get(4)
1133                 ), change.getList());
1134             }
1135         }));
1136 
1137         model.selectIndices(4, 3, 2);
1138     }
1139 
1140     @Test public void test_rt39548_positiveValue_outOfRange() {
1141         // for this test we want there to be no data in the controls
1142         clearModelData();
1143 
1144         model.clearAndSelect(10);
1145     }
1146 
1147     @Test public void test_rt39548_negativeValue() {
1148         // for this test we want there to be no data in the controls
1149         clearModelData();
1150 
1151         model.clearAndSelect(-1);
1152     }
1153 
1154     @Ignore
1155     @Test public void test_rt38884_invalidChange() {
1156         model.select(3);
1157         int removedSize = model.getSelectedItems().size();
1158         ListChangeListener l = (ListChangeListener.Change c) -> {
1159             c.next();
1160             assertEquals(removedSize, c.getRemovedSize());
1161         };
1162         model.getSelectedItems().addListener(l);
1163         clearModelData();
1164     }
1165 
1166     private void clearModelData() {
1167         listView.getItems().clear();
1168         tableView.getItems().clear();
1169         treeView.setRoot(null);
1170         treeTableView.setRoot(null);
1171     }
1172 
1173     @Test public void test_rt40804() {
1174         StageLoader sl = new StageLoader(currentControl);
1175         model.setSelectionMode(SelectionMode.MULTIPLE);
1176         model.select(0);
1177         model.select(1);
1178         model.clearSelection();
1179         final Thread.UncaughtExceptionHandler exceptionHandler = ControlTestUtils.setHandler();
1180         try {
1181             model.select(3); // this is where the test failed
1182         } finally {
1183             ControlTestUtils.resetHandler(exceptionHandler);
1184         }
1185 
1186         sl.dispose();
1187     }
1188 }