1 /*
   2  * Copyright (c) 2011, 2017, 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 test.javafx.scene.control;
  27 
  28 import com.sun.javafx.scene.control.VirtualScrollBar;
  29 import com.sun.javafx.scene.control.behavior.ListCellBehavior;
  30 import com.sun.javafx.tk.Toolkit;
  31 import java.util.ArrayList;
  32 import java.util.Arrays;
  33 import java.util.Collections;
  34 import java.util.List;
  35 import java.util.ListIterator;
  36 import java.util.NoSuchElementException;
  37 import javafx.application.Platform;
  38 import javafx.beans.binding.Bindings;
  39 import javafx.beans.property.ObjectProperty;
  40 import javafx.beans.property.ReadOnlyBooleanWrapper;
  41 import javafx.beans.property.SimpleObjectProperty;
  42 import javafx.collections.FXCollections;
  43 import javafx.collections.ListChangeListener;
  44 import javafx.collections.ObservableList;
  45 import javafx.collections.transformation.SortedList;
  46 import javafx.scene.control.Button;
  47 import javafx.scene.control.FocusModel;
  48 import javafx.scene.control.IndexedCell;
  49 import javafx.scene.control.ListCell;
  50 import javafx.scene.control.ListCellShim;
  51 import javafx.scene.control.ListView;
  52 import javafx.scene.control.ListViewShim;
  53 import javafx.scene.control.MultipleSelectionModel;
  54 import javafx.scene.control.SelectionMode;
  55 import javafx.scene.control.SelectionModel;
  56 import javafx.scene.control.TextField;
  57 import javafx.scene.control.cell.CheckBoxListCell;
  58 import javafx.scene.control.cell.ComboBoxListCell;
  59 import javafx.scene.control.cell.TextFieldListCell;
  60 import javafx.scene.image.ImageView;
  61 import javafx.scene.input.KeyCode;
  62 import javafx.scene.layout.VBox;
  63 import javafx.scene.paint.Color;
  64 import javafx.scene.shape.Rectangle;
  65 import javafx.util.Callback;
  66 import static org.junit.Assert.assertArrayEquals;
  67 import static org.junit.Assert.assertEquals;
  68 import static org.junit.Assert.assertFalse;
  69 import static org.junit.Assert.assertNotNull;
  70 import static org.junit.Assert.assertNull;
  71 import static org.junit.Assert.assertSame;
  72 import static org.junit.Assert.assertTrue;
  73 import static org.junit.Assert.fail;
  74 import org.junit.Before;
  75 import org.junit.Test;
  76 import static test.com.sun.javafx.scene.control.infrastructure.ControlTestUtils.assertStyleClassContains;
  77 import test.com.sun.javafx.scene.control.infrastructure.KeyEventFirer;
  78 import test.com.sun.javafx.scene.control.infrastructure.KeyModifier;
  79 import test.com.sun.javafx.scene.control.infrastructure.StageLoader;
  80 import test.com.sun.javafx.scene.control.infrastructure.VirtualFlowTestUtils;
  81 import test.com.sun.javafx.scene.control.test.Person;
  82 import test.com.sun.javafx.scene.control.test.RT_22463_Person;
  83 
  84 public class ListViewTest {
  85     private ListView<String> listView;
  86     private MultipleSelectionModel<String> sm;
  87     private FocusModel<String> fm;
  88 
  89     @Before public void setup() {
  90         listView = new ListView<>();
  91         sm = listView.getSelectionModel();
  92         fm = listView.getFocusModel();
  93     }
  94 
  95 
  96     /*********************************************************************
  97      * Tests for the constructors                                        *
  98      ********************************************************************/
  99 
 100     @Test public void noArgConstructorSetsTheStyleClass() {
 101         assertStyleClassContains(listView, "list-view");
 102     }
 103 
 104     @Test public void noArgConstructorSetsNonNullSelectionModel() {
 105         assertNotNull(sm);
 106     }
 107 
 108     @Test public void noArgConstructorSetsNonNullItems() {
 109         assertNotNull(listView.getItems());
 110     }
 111 
 112     @Test public void noArgConstructor_selectedItemIsNull() {
 113         assertNull(sm.getSelectedItem());
 114     }
 115 
 116     @Test public void noArgConstructor_selectedIndexIsNegativeOne() {
 117         assertEquals(-1, sm.getSelectedIndex());
 118     }
 119 
 120     @Test public void singleArgConstructorSetsTheStyleClass() {
 121         final ListView<String> b2 = new ListView<>(FXCollections.observableArrayList("Hi"));
 122         assertStyleClassContains(b2, "list-view");
 123     }
 124 
 125     @Test public void singleArgConstructorSetsNonNullSelectionModel() {
 126         final ListView<String> b2 = new ListView<>(FXCollections.<String>observableArrayList("Hi"));
 127         assertNotNull(b2.getSelectionModel());
 128     }
 129 
 130     @Test public void singleArgConstructorAllowsNullItems() {
 131         final ListView<String> b2 = new ListView<String>(null);
 132         assertNull(b2.getItems());
 133     }
 134 
 135     @Test public void singleArgConstructorTakesItems() {
 136         ObservableList<String> items = FXCollections.observableArrayList("Hi");
 137         final ListView<String> b2 = new ListView<>(items);
 138         assertSame(items, b2.getItems());
 139     }
 140 
 141     @Test public void singleArgConstructor_selectedItemIsNull() {
 142         final ListView<String> b2 = new ListView<>(FXCollections.observableArrayList("Hi"));
 143         assertNull(b2.getSelectionModel().getSelectedItem());
 144     }
 145 
 146     @Test public void singleArgConstructor_selectedIndexIsNegativeOne() {
 147         final ListView<String> b2 = new ListView<>(FXCollections.observableArrayList("Hi"));
 148         assertEquals(-1, b2.getSelectionModel().getSelectedIndex());
 149     }
 150 
 151     /*********************************************************************
 152      * Tests for selection model                                         *
 153      ********************************************************************/
 154 
 155     @Test public void selectionModelCanBeNull() {
 156         listView.setSelectionModel(null);
 157         assertNull(listView.getSelectionModel());
 158     }
 159 
 160     @Test public void selectionModelCanBeBound() {
 161         MultipleSelectionModel<String> sm = ListViewShim.<String>getListViewBitSetSelectionModel(listView);
 162         ObjectProperty<MultipleSelectionModel<String>> other = new SimpleObjectProperty<MultipleSelectionModel<String>>(sm);
 163         listView.selectionModelProperty().bind(other);
 164         assertSame(sm, sm);
 165     }
 166 
 167     @Test public void selectionModelCanBeChanged() {
 168         MultipleSelectionModel<String> sm = ListViewShim.<String>getListViewBitSetSelectionModel(listView);
 169         listView.setSelectionModel(sm);
 170         assertSame(sm, sm);
 171     }
 172 
 173     @Test public void canSetSelectedItemToAnItemEvenWhenThereAreNoItems() {
 174         final String randomString = new String("I AM A CRAZY RANDOM STRING");
 175         sm.select(randomString);
 176         assertEquals(-1, sm.getSelectedIndex());
 177         assertSame(randomString, sm.getSelectedItem());
 178     }
 179 
 180     @Test public void canSetSelectedItemToAnItemNotInTheDataModel() {
 181         listView.getItems().addAll("Apple", "Orange", "Banana");
 182         final String randomString = new String("I AM A CRAZY RANDOM STRING");
 183         sm.select(randomString);
 184         assertEquals(-1, sm.getSelectedIndex());
 185         assertSame(randomString, sm.getSelectedItem());
 186     }
 187 
 188     @Test public void settingTheSelectedItemToAnItemInItemsResultsInTheCorrectSelectedIndex() {
 189         listView.getItems().addAll("Apple", "Orange", "Banana");
 190         sm.select("Orange");
 191         assertEquals(1, sm.getSelectedIndex());
 192         assertSame("Orange", sm.getSelectedItem());
 193     }
 194 
 195     @Test public void settingTheSelectedItemToANonexistantItemAndThenSettingItemsWhichContainsItResultsInCorrectSelectedIndex() {
 196         sm.select("Orange");
 197         listView.getItems().addAll("Apple", "Orange", "Banana");
 198         assertEquals(1, sm.getSelectedIndex());
 199         assertSame("Orange", sm.getSelectedItem());
 200     }
 201 
 202     @Test public void ensureSelectionClearsWhenAllItemsAreRemoved_selectIndex0() {
 203         listView.getItems().addAll("Apple", "Orange", "Banana");
 204         sm.select(0);
 205         listView.getItems().clear();
 206         assertEquals(-1, sm.getSelectedIndex());
 207         assertEquals(null, sm.getSelectedItem());
 208     }
 209 
 210     @Test public void ensureSelectionClearsWhenAllItemsAreRemoved_selectIndex2() {
 211         listView.getItems().addAll("Apple", "Orange", "Banana");
 212         sm.select(2);
 213         listView.getItems().clear();
 214         assertEquals(-1, sm.getSelectedIndex());
 215         assertEquals(null, sm.getSelectedItem());
 216     }
 217 
 218     @Test public void ensureSelectedItemRemainsAccurateWhenItemsAreCleared() {
 219         listView.getItems().addAll("Apple", "Orange", "Banana");
 220         sm.select(2);
 221         listView.getItems().clear();
 222         assertNull(sm.getSelectedItem());
 223         assertEquals(-1, sm.getSelectedIndex());
 224 
 225         listView.getItems().addAll("Kiwifruit", "Mandarin", "Pineapple");
 226         sm.select(2);
 227         assertEquals("Pineapple", sm.getSelectedItem());
 228     }
 229 
 230     @Test public void ensureSelectionShiftsDownWhenOneNewItemIsAdded() {
 231         listView.getItems().addAll("Apple", "Orange", "Banana");
 232         sm.select(1);
 233         assertEquals(1, sm.getSelectedIndex());
 234         assertEquals("Orange", sm.getSelectedItem());
 235 
 236         listView.getItems().add(0, "Kiwifruit");
 237         assertEquals(2, sm.getSelectedIndex());
 238         assertEquals("Orange", sm.getSelectedItem());
 239     }
 240 
 241     @Test public void ensureSelectionShiftsDownWhenMultipleNewItemAreAdded() {
 242         listView.getItems().addAll("Apple", "Orange", "Banana");
 243         sm.select(1);
 244         assertEquals(1, sm.getSelectedIndex());
 245         assertEquals("Orange", sm.getSelectedItem());
 246 
 247         listView.getItems().addAll(0, Arrays.asList("Kiwifruit", "Pineapple", "Mandarin"));
 248         assertEquals("Orange", sm.getSelectedItem());
 249         assertEquals(4, sm.getSelectedIndex());
 250     }
 251 
 252     @Test public void ensureSelectionShiftsUpWhenOneItemIsRemoved() {
 253         listView.getItems().addAll("Apple", "Orange", "Banana");
 254         sm.select(1);
 255         assertEquals(1, sm.getSelectedIndex());
 256         assertEquals("Orange", sm.getSelectedItem());
 257 
 258         listView.getItems().remove("Apple");
 259         assertEquals(0, sm.getSelectedIndex());
 260         assertEquals("Orange", sm.getSelectedItem());
 261     }
 262 
 263     @Test public void ensureSelectionShiftsUpWheMultipleItemsAreRemoved() {
 264         listView.getItems().addAll("Apple", "Orange", "Banana");
 265         sm.select(2);
 266         assertEquals(2, sm.getSelectedIndex());
 267         assertEquals("Banana", sm.getSelectedItem());
 268 
 269         listView.getItems().removeAll(Arrays.asList("Apple", "Orange"));
 270         assertEquals(0, sm.getSelectedIndex());
 271         assertEquals("Banana", sm.getSelectedItem());
 272     }
 273 
 274     @Test public void ensureSelectionIsCorrectWhenItemsChange() {
 275         listView.setItems(FXCollections.observableArrayList("Item 1"));
 276         sm.select(0);
 277         assertEquals("Item 1", sm.getSelectedItem());
 278 
 279         listView.setItems(FXCollections.observableArrayList("Item 2"));
 280         assertEquals(-1, sm.getSelectedIndex());
 281         assertNull(sm.getSelectedItem());
 282         assertEquals(0, fm.getFocusedIndex());
 283         assertEquals("Item 2", fm.getFocusedItem());
 284     }
 285 
 286     @Test public void test_rt15793() {
 287         // ListView selectedIndex is 0 although the items list is empty
 288         final ListView lv = new ListView();
 289         final ObservableList list = FXCollections.observableArrayList();
 290         lv.setItems(list);
 291         list.add("toto");
 292         lv.getSelectionModel().select(0);
 293         assertEquals(0, lv.getSelectionModel().getSelectedIndex());
 294         list.remove(0);
 295         assertEquals(-1, lv.getSelectionModel().getSelectedIndex());
 296     }
 297 
 298     @Test public void test_rt17522_focusShouldMoveWhenItemAddedAtFocusIndex() {
 299         final ListView lv = new ListView();
 300         FocusModel fm = lv.getFocusModel();
 301         lv.getItems().add("row1");
 302         fm.focus(0);
 303         assertTrue(fm.isFocused(0));
 304 
 305         lv.getItems().add(0, "row0");
 306         assertTrue(fm.isFocused(1));
 307     }
 308 
 309     @Test public void test_rt17522_focusShouldMoveWhenItemAddedBeforeFocusIndex() {
 310         final ListView lv = new ListView();
 311         FocusModel fm = lv.getFocusModel();
 312         lv.getItems().addAll("row1", "row2");
 313         fm.focus(1);
 314         assertTrue(fm.isFocused(1));
 315         assertEquals("row2", fm.getFocusedItem());
 316 
 317         lv.getItems().add(1, "row0");
 318         assertTrue(fm.isFocused(2));
 319         assertEquals("row2", fm.getFocusedItem());
 320         assertFalse(fm.isFocused(1));
 321     }
 322 
 323     @Test public void test_rt17522_focusShouldNotMoveWhenItemAddedAfterFocusIndex() {
 324         final ListView lv = new ListView();
 325         FocusModel fm = lv.getFocusModel();
 326         lv.getItems().addAll("row1");
 327         fm.focus(0);
 328         assertTrue(fm.isFocused(0));
 329         assertEquals("row1", fm.getFocusedItem());
 330 
 331         lv.getItems().add(1, "row2");
 332         assertTrue(fm.isFocused(0));
 333         assertEquals("row1", fm.getFocusedItem());
 334         assertFalse(fm.isFocused(1));
 335     }
 336 
 337     @Test public void test_rt17522_focusShouldBeResetWhenFocusedItemIsRemoved() {
 338         final ListView lv = new ListView();
 339         FocusModel fm = lv.getFocusModel();
 340         lv.getItems().add("row1");
 341         fm.focus(0);
 342         assertTrue(fm.isFocused(0));
 343 
 344         lv.getItems().remove("row1");
 345         assertTrue(fm.getFocusedIndex() == -1);
 346         assertNull(fm.getFocusedItem());
 347     }
 348 
 349     @Test public void test_rt17522_focusShouldMoveWhenItemRemovedBeforeFocusIndex() {
 350         final ListView lv = new ListView();
 351         FocusModel fm = lv.getFocusModel();
 352         lv.getItems().addAll("row1", "row2");
 353         fm.focus(1);
 354         assertTrue(fm.isFocused(1));
 355         assertEquals("row2", fm.getFocusedItem());
 356 
 357         lv.getItems().remove("row1");
 358         assertTrue(fm.isFocused(0));
 359         assertEquals("row2", fm.getFocusedItem());
 360     }
 361 
 362     @Test public void test_rt17522_focusShouldNotMoveWhenItemRemovedAfterFocusIndex() {
 363         final ListView lv = new ListView();
 364         FocusModel fm = lv.getFocusModel();
 365         lv.getItems().addAll("row1", "row2");
 366         fm.focus(0);
 367         assertTrue(fm.isFocused(0));
 368         assertEquals("row1", fm.getFocusedItem());
 369 
 370         lv.getItems().remove("row2");
 371         assertTrue(fm.isFocused(0));
 372         assertEquals("row1", fm.getFocusedItem());
 373     }
 374 
 375     @Test public void test_rt18385() {
 376         listView.getItems().addAll("row1", "row2", "row3");
 377         sm.select(1);
 378         listView.getItems().add("Another Row");
 379         assertEquals(1, sm.getSelectedIndices().size());
 380         assertEquals(1, sm.getSelectedItems().size());
 381     }
 382 
 383     @Test public void test_rt18339_onlyEditWhenListViewIsEditable_editableIsFalse() {
 384         listView.setEditable(false);
 385         listView.edit(1);
 386         assertEquals(-1, listView.getEditingIndex());
 387     }
 388 
 389     @Test public void test_rt18339_onlyEditWhenListViewIsEditable_editableIsTrue() {
 390         listView.setEditable(true);
 391         listView.edit(1);
 392         assertEquals(1, listView.getEditingIndex());
 393     }
 394 
 395     @Test public void test_rt14451() {
 396         listView.getItems().addAll("Apple", "Orange", "Banana");
 397         sm.setSelectionMode(SelectionMode.MULTIPLE);
 398         sm.selectRange(0, 2); // select from 0 (inclusive) to 2 (exclusive)
 399         assertEquals(2, sm.getSelectedIndices().size());
 400     }
 401 
 402     private int rt_18969_hitCount = 0;
 403     @Test public void test_rt18969() {
 404         rt_18969_hitCount = 0;
 405         ObservableList<String> emptyModel = FXCollections.observableArrayList();
 406         listView.setItems(emptyModel);
 407         assertTrue(listView.getItems().isEmpty());
 408 
 409         sm.selectedItemProperty().addListener((observable, oldValue, newValue) -> {
 410             rt_18969_hitCount++;
 411         });
 412 
 413         ObservableList<String> mod = FXCollections.observableArrayList();
 414         mod.add(System.currentTimeMillis()+"");
 415         listView.getItems().setAll(mod);
 416 
 417         sm.select(0);
 418         assertTrue(sm.isSelected(0));
 419         assertEquals(1, rt_18969_hitCount);
 420 
 421         // sleep for 100ms so that the currentTimeMillis is guaranteed to be
 422         // a different value than the first one
 423         try {
 424             Thread.sleep(100);
 425         } catch (InterruptedException ex) {
 426             ex.printStackTrace();
 427         }
 428 
 429         // the list is totally changing (it is being cleared), so we should
 430         // be nulling out the selection model state
 431         mod = FXCollections.observableArrayList();
 432         mod.add(System.currentTimeMillis()+"");
 433         listView.getItems().setAll(mod);
 434 
 435         // it should be two, as there is no null event in between (although there
 436         // used to be, so the test used to be for three hits)
 437         assertEquals(2, rt_18969_hitCount);
 438     }
 439 
 440     @Test public void test_rt21586() {
 441         listView.getItems().setAll("Apple", "Orange", "Banana");
 442         listView.getSelectionModel().select(1);
 443         assertEquals(1, listView.getSelectionModel().getSelectedIndex());
 444         assertEquals("Orange", listView.getSelectionModel().getSelectedItem());
 445 
 446         listView.getItems().setAll("Kiwifruit", "Pineapple", "Grape");
 447         assertEquals(-1, listView.getSelectionModel().getSelectedIndex());
 448         assertNull(listView.getSelectionModel().getSelectedItem());
 449     }
 450 
 451     @Test public void test_rt27820_1() {
 452         listView.getItems().setAll("Apple", "Orange");
 453         listView.getSelectionModel().select(0);
 454         assertEquals(1, listView.getSelectionModel().getSelectedItems().size());
 455         assertEquals("Apple", listView.getSelectionModel().getSelectedItem());
 456 
 457         listView.getItems().clear();
 458         assertEquals(0, listView.getSelectionModel().getSelectedItems().size());
 459         assertNull(listView.getSelectionModel().getSelectedItem());
 460     }
 461 
 462     @Test public void test_rt27820_2() {
 463         listView.getItems().setAll("Apple", "Orange");
 464         listView.getSelectionModel().select(1);
 465         assertEquals(1, listView.getSelectionModel().getSelectedItems().size());
 466         assertEquals("Orange", listView.getSelectionModel().getSelectedItem());
 467 
 468         listView.getItems().clear();
 469         assertEquals(0, listView.getSelectionModel().getSelectedItems().size());
 470         assertNull(listView.getSelectionModel().getSelectedItem());
 471     }
 472 
 473     @Test public void test_rt28534() {
 474         ListView<Person> list = new ListView<Person>();
 475         list.setItems(FXCollections.observableArrayList(
 476                 new Person("Jacob", "Smith", "jacob.smith@example.com"),
 477                 new Person("Isabella", "Johnson", "isabella.johnson@example.com"),
 478                 new Person("Ethan", "Williams", "ethan.williams@example.com"),
 479                 new Person("Emma", "Jones", "emma.jones@example.com"),
 480                 new Person("Michael", "Brown", "michael.brown@example.com")));
 481 
 482         VirtualFlowTestUtils.assertRowsNotEmpty(list, 0, 5); // rows 0 - 5 should be filled
 483         VirtualFlowTestUtils.assertRowsEmpty(list, 5, -1); // rows 5+ should be empty
 484 
 485         // now we replace the data and expect the cells that have no data
 486         // to be empty
 487         list.setItems(FXCollections.observableArrayList(
 488                 new Person("*_*Emma", "Jones", "emma.jones@example.com"),
 489                 new Person("_Michael", "Brown", "michael.brown@example.com")));
 490 
 491         VirtualFlowTestUtils.assertRowsNotEmpty(list, 0, 2); // rows 0 - 2 should be filled
 492         VirtualFlowTestUtils.assertRowsEmpty(list, 2, -1); // rows 2+ should be empty
 493     }
 494 
 495     @Test public void test_rt22463() {
 496         final ListView<RT_22463_Person> list = new ListView<RT_22463_Person>();
 497 
 498         // before the change things display fine
 499         RT_22463_Person p1 = new RT_22463_Person();
 500         p1.setId(1l);
 501         p1.setName("name1");
 502         RT_22463_Person p2 = new RT_22463_Person();
 503         p2.setId(2l);
 504         p2.setName("name2");
 505         list.setItems(FXCollections.observableArrayList(p1, p2));
 506         VirtualFlowTestUtils.assertCellTextEquals(list, 0, "name1");
 507         VirtualFlowTestUtils.assertCellTextEquals(list, 1, "name2");
 508 
 509         // now we change the persons but they are still equal as the ID's don't
 510         // change - but the items list is cleared so the cells should update
 511         RT_22463_Person new_p1 = new RT_22463_Person();
 512         new_p1.setId(1l);
 513         new_p1.setName("updated name1");
 514         RT_22463_Person new_p2 = new RT_22463_Person();
 515         new_p2.setId(2l);
 516         new_p2.setName("updated name2");
 517         list.getItems().clear();
 518         list.setItems(FXCollections.observableArrayList(new_p1, new_p2));
 519         VirtualFlowTestUtils.assertCellTextEquals(list, 0, "updated name1");
 520         VirtualFlowTestUtils.assertCellTextEquals(list, 1, "updated name2");
 521     }
 522 
 523     @Test public void test_rt28637() {
 524         ObservableList<String> items = FXCollections.observableArrayList("String1", "String2", "String3", "String4");
 525 
 526         final ListView<String> listView = new ListView<String>();
 527         listView.setItems(items);
 528 
 529         listView.getSelectionModel().select(0);
 530         assertEquals("String1", listView.getSelectionModel().getSelectedItem());
 531         assertEquals("String1", listView.getSelectionModel().getSelectedItems().get(0));
 532         assertEquals(0, listView.getSelectionModel().getSelectedIndex());
 533 
 534         items.remove(listView.getSelectionModel().getSelectedItem());
 535         assertEquals("String2", listView.getSelectionModel().getSelectedItem());
 536         assertEquals("String2", listView.getSelectionModel().getSelectedItems().get(0));
 537         assertEquals(0, listView.getSelectionModel().getSelectedIndex());
 538     }
 539 
 540     @Test public void test_rt28819_1() {
 541         ObservableList<String> emptyModel = FXCollections.observableArrayList();
 542 
 543         final ListView<String> listView = new ListView<String>();
 544         listView.setItems(emptyModel);
 545         VirtualFlowTestUtils.assertRowsEmpty(listView, 0, 5);
 546 
 547         ObservableList<String> mod = FXCollections.observableArrayList();
 548         String value = System.currentTimeMillis()+"";
 549         mod.add(value);
 550         listView.setItems(mod);
 551         VirtualFlowTestUtils.assertCellCount(listView, 1);
 552         VirtualFlowTestUtils.assertCellTextEquals(listView, 0, value);
 553     }
 554 
 555     @Test public void test_rt28819_2() {
 556         ObservableList<String> emptyModel = FXCollections.observableArrayList();
 557 
 558         final ListView<String> listView = new ListView<String>();
 559         listView.setItems(emptyModel);
 560         VirtualFlowTestUtils.assertRowsEmpty(listView, 0, 5);
 561 
 562         ObservableList<String> mod1 = FXCollections.observableArrayList();
 563         String value1 = System.currentTimeMillis()+"";
 564         mod1.add(value1);
 565         listView.getItems().setAll(mod1);
 566         VirtualFlowTestUtils.assertCellCount(listView, 1);
 567         VirtualFlowTestUtils.assertCellTextEquals(listView, 0, value1);
 568     }
 569 
 570     @Test public void test_rt29390() {
 571         ObservableList<String> items = FXCollections.observableArrayList(
 572                 "String1", "String2", "String3", "String4",
 573                 "String1", "String2", "String3", "String4",
 574                 "String1", "String2", "String3", "String4",
 575                 "String1", "String2", "String3", "String4"
 576         );
 577 
 578         final ListView<String> listView = new ListView<String>(items);
 579         listView.setMaxHeight(50);
 580         listView.setPrefHeight(50);
 581 
 582         // we want the vertical scrollbar
 583         VirtualScrollBar scrollBar = VirtualFlowTestUtils.getVirtualFlowVerticalScrollbar(listView);
 584 
 585         assertNotNull(scrollBar);
 586         assertTrue(scrollBar.isVisible());
 587         assertTrue(scrollBar.getVisibleAmount() > 0.0);
 588         assertTrue(scrollBar.getVisibleAmount() < 1.0);
 589 
 590         // this next test is likely to be brittle, but we'll see...If it is the
 591         // cause of failure then it can be commented out
 592         assertEquals(0.125, scrollBar.getVisibleAmount(), 0.0);
 593     }
 594 
 595     @Test public void test_rt30400() {
 596         // create a listview that'll render cells using the check box cell factory
 597         ObservableList<String> items = FXCollections.observableArrayList("String1");
 598         final ListView<String> listView = new ListView<String>(items);
 599         listView.setMinHeight(100);
 600         listView.setPrefHeight(100);
 601         listView.setCellFactory(CheckBoxListCell.forListView(param -> new ReadOnlyBooleanWrapper(true)));
 602 
 603         // because only the first row has data, all other rows should be
 604         // empty (and not contain check boxes - we just check the first four here)
 605         VirtualFlowTestUtils.assertRowsNotEmpty(listView, 0, 1);
 606         VirtualFlowTestUtils.assertCellNotEmpty(VirtualFlowTestUtils.getCell(listView, 0));
 607         VirtualFlowTestUtils.assertCellEmpty(VirtualFlowTestUtils.getCell(listView, 1));
 608         VirtualFlowTestUtils.assertCellEmpty(VirtualFlowTestUtils.getCell(listView, 2));
 609         VirtualFlowTestUtils.assertCellEmpty(VirtualFlowTestUtils.getCell(listView, 3));
 610     }
 611 
 612     @Test public void test_rt29420() {
 613         final ListView<String> listView = new ListView<String>();
 614 
 615         VBox vbox = new VBox(listView);
 616         StageLoader sl = new StageLoader(vbox);
 617 
 618         // the initial width of a ListView should be the golden rectangle where
 619         // the height is hardcoded to be 400
 620         final double initialWidth = listView.prefWidth(-1);
 621         assertEquals(400 * 0.618033987, initialWidth, 0.00);
 622 
 623         // add in some items, and re-measure - seeing as the items are narrow,
 624         // the width shouldn't change
 625         listView.getItems().addAll("one", "two", "three", "four", "five", "six");
 626         Toolkit.getToolkit().firePulse();
 627         final double withContentWidth = listView.prefWidth(-1);
 628         assertEquals(initialWidth, withContentWidth, 0.00);
 629 
 630         // remove the items - and the width should remain the same
 631         listView.getItems().clear();
 632         Toolkit.getToolkit().firePulse();
 633         final double afterEmptiedWidth = listView.prefWidth(-1);
 634         assertEquals(initialWidth, afterEmptiedWidth, 0.00);
 635 
 636         sl.dispose();
 637     }
 638 
 639     @Test public void test_rt31165() {
 640         final ObservableList names = FXCollections.observableArrayList("Adam", "Alex", "Alfred", "Albert");
 641         final ObservableList data = FXCollections.observableArrayList();
 642         for (int i = 0; i < 18; i++) {
 643             data.add(""+i);
 644         }
 645 
 646         final ListView listView = new ListView(data);
 647         listView.setPrefSize(200, 250);
 648         listView.setEditable(true);
 649         listView.setCellFactory(ComboBoxListCell.forListView(names));
 650 
 651         IndexedCell cell = VirtualFlowTestUtils.getCell(listView, 1);
 652         assertEquals("1", cell.getText());
 653         assertFalse(cell.isEditing());
 654 
 655         listView.edit(1);
 656 
 657         assertEquals(1, listView.getEditingIndex());
 658         assertTrue(cell.isEditing());
 659 
 660         VirtualFlowTestUtils.getVirtualFlow(listView).requestLayout();
 661         Toolkit.getToolkit().firePulse();
 662 
 663         assertEquals(1, listView.getEditingIndex());
 664         assertTrue(cell.isEditing());
 665     }
 666 
 667     @Test public void test_rt31471() {
 668         final ObservableList names = FXCollections.observableArrayList("Adam", "Alex", "Alfred", "Albert");
 669         final ListView listView = new ListView(names);
 670 
 671         IndexedCell cell = VirtualFlowTestUtils.getCell(listView, 0);
 672         assertEquals("Adam", cell.getItem());
 673 
 674         listView.setFixedCellSize(50);
 675 
 676         VirtualFlowTestUtils.getVirtualFlow(listView).requestLayout();
 677         Toolkit.getToolkit().firePulse();
 678 
 679         assertEquals("Adam", cell.getItem());
 680         assertEquals(50, cell.getHeight(), 0.00);
 681     }
 682 
 683     private int rt_31200_count = 0;
 684     @Test public void test_rt_31200() {
 685         final ListView listView = new ListView();
 686         listView.setCellFactory(new Callback<ListView<String>, ListCell<String>>() {
 687             @Override
 688             public ListCell<String> call(ListView<String> param) {
 689                 return new ListCellShim<String>() {
 690                     ImageView view = new ImageView();
 691                     { setGraphic(view); };
 692 
 693                     @Override
 694                     public void updateItem(String item, boolean empty) {
 695                         if (getItem() == null ? item == null : getItem().equals(item)) {
 696                             rt_31200_count++;
 697                         }
 698                         super.updateItem(item, empty);
 699                         if (item == null || empty) {
 700                             view.setImage(null);
 701                             setText(null);
 702                         } else {
 703                             setText(item);
 704                         }
 705                     }
 706                 };
 707             }
 708         });
 709         listView.getItems().setAll("one", "two", "three", "four", "five");
 710 
 711         StageLoader sl = new StageLoader(listView);
 712 
 713         assertEquals(24, rt_31200_count);
 714 
 715         // resize the stage
 716         sl.getStage().setHeight(250);
 717         Toolkit.getToolkit().firePulse();
 718         sl.getStage().setHeight(50);
 719         Toolkit.getToolkit().firePulse();
 720         assertEquals(24, rt_31200_count);
 721 
 722         sl.dispose();
 723     }
 724 
 725     @Test public void test_rt_30484() {
 726         final ListView listView = new ListView();
 727         listView.setCellFactory(new Callback<ListView<String>, ListCell<String>>() {
 728             @Override public ListCell<String> call(ListView<String> param) {
 729                 return new ListCellShim<String>() {
 730                     Rectangle graphic = new Rectangle(10, 10, Color.RED);
 731                     { setGraphic(graphic); };
 732 
 733                     @Override public void updateItem(String item, boolean empty) {
 734                         super.updateItem(item, empty);
 735                         if (item == null || empty) {
 736                             graphic.setVisible(false);
 737                             setText(null);
 738                         } else {
 739                             graphic.setVisible(true);
 740                             setText(item);
 741                         }
 742                     }
 743                 };
 744             }
 745         });
 746 
 747         // First two rows have content, so the graphic should show.
 748         // All other rows have no content, so graphic should not show.
 749         listView.getItems().setAll("one", "two");
 750 
 751         VirtualFlowTestUtils.assertGraphicIsVisible(listView, 0);
 752         VirtualFlowTestUtils.assertGraphicIsVisible(listView, 1);
 753         VirtualFlowTestUtils.assertGraphicIsNotVisible(listView, 2);
 754         VirtualFlowTestUtils.assertGraphicIsNotVisible(listView, 3);
 755         VirtualFlowTestUtils.assertGraphicIsNotVisible(listView, 4);
 756         VirtualFlowTestUtils.assertGraphicIsNotVisible(listView, 5);
 757     }
 758 
 759     private int rt_29650_start_count = 0;
 760     private int rt_29650_commit_count = 0;
 761     private int rt_29650_cancel_count = 0;
 762     @Test public void test_rt_29650() {
 763         listView.setOnEditStart(t -> {
 764             rt_29650_start_count++;
 765         });
 766         listView.setOnEditCommit(t -> {
 767             rt_29650_commit_count++;
 768         });
 769         listView.setOnEditCancel(t -> {
 770             rt_29650_cancel_count++;
 771         });
 772 
 773         listView.getItems().setAll("one", "two", "three", "four", "five");
 774         listView.setEditable(true);
 775         listView.setCellFactory(TextFieldListCell.forListView());
 776 
 777         StageLoader sl = new StageLoader(listView);
 778 
 779         listView.edit(0);
 780 
 781         Toolkit.getToolkit().firePulse();
 782 
 783         ListCell rootCell = (ListCell) VirtualFlowTestUtils.getCell(listView, 0);
 784         TextField textField = (TextField) rootCell.getGraphic();
 785         textField.setText("Testing!");
 786         KeyEventFirer keyboard = new KeyEventFirer(textField);
 787         keyboard.doKeyPress(KeyCode.ENTER);
 788 
 789         // TODO should the following assert be enabled?
 790 //        assertEquals("Testing!", listView.getItems().get(0));
 791         assertEquals(1, rt_29650_start_count);
 792         assertEquals(1, rt_29650_commit_count);
 793         assertEquals(0, rt_29650_cancel_count);
 794 
 795         sl.dispose();
 796     }
 797 
 798     @Test public void test_rt35039() {
 799         final List<String> data = new ArrayList<>();
 800         data.add("aabbaa");
 801         data.add("bbc");
 802 
 803         final ListView<String> listView = new ListView<>();
 804         listView.setItems(FXCollections.observableArrayList(data));
 805 
 806         StageLoader sl = new StageLoader(listView);
 807 
 808         // selection starts off on row -1
 809         assertNull(listView.getSelectionModel().getSelectedItem());
 810 
 811         // select "bbc" and ensure everything is set to that
 812         listView.getSelectionModel().select(1);
 813         assertEquals("bbc", listView.getSelectionModel().getSelectedItem());
 814 
 815         // change the items list - but retain the same content. We expect
 816         // that "bbc" remains selected as it is still in the list
 817         listView.setItems(FXCollections.observableArrayList(data));
 818         assertEquals("bbc", listView.getSelectionModel().getSelectedItem());
 819 
 820         sl.dispose();
 821     }
 822 
 823     @Test public void test_rt35857() {
 824         ObservableList<String> fxList = FXCollections.observableArrayList("A", "B", "C");
 825         final ListView<String> listView = new ListView<String>(fxList);
 826 
 827         listView.getSelectionModel().select(0);
 828 
 829         ObservableList<String> selectedItems = listView.getSelectionModel().getSelectedItems();
 830         assertEquals(1, selectedItems.size());
 831         assertEquals("A", selectedItems.get(0));
 832 
 833         listView.getItems().removeAll(selectedItems);
 834         assertEquals(2, fxList.size());
 835         assertEquals("B", fxList.get(0));
 836         assertEquals("C", fxList.get(1));
 837     }
 838 
 839     private int rt_35889_cancel_count = 0;
 840     @Test public void test_rt35889() {
 841         final ListView<String> textFieldListView = new ListView<String>();
 842         textFieldListView.setItems(FXCollections.observableArrayList("A", "B", "C"));
 843         textFieldListView.setEditable(true);
 844         textFieldListView.setCellFactory(TextFieldListCell.forListView());
 845         textFieldListView.setOnEditCancel(t -> {
 846             rt_35889_cancel_count++;
 847             System.out.println("On Edit Cancel: " + t);
 848         });
 849 
 850         ListCell cell0 = (ListCell) VirtualFlowTestUtils.getCell(textFieldListView, 0);
 851         assertNull(cell0.getGraphic());
 852         assertEquals("A", cell0.getText());
 853 
 854         textFieldListView.edit(0);
 855         TextField textField = (TextField) cell0.getGraphic();
 856         assertNotNull(textField);
 857 
 858         assertEquals(0, rt_35889_cancel_count);
 859 
 860         textField.setText("Z");
 861         KeyEventFirer keyboard = new KeyEventFirer(textField);
 862         keyboard.doKeyPress(KeyCode.ENTER);
 863 
 864         assertEquals(0, rt_35889_cancel_count);
 865     }
 866 
 867     @Test public void test_rt25679() {
 868         Button focusBtn = new Button("Focus here");
 869 
 870         final ListView<String> listView = new ListView<String>();
 871         SelectionModel sm = listView.getSelectionModel();
 872         listView.setItems(FXCollections.observableArrayList("A", "B", "C"));
 873 
 874         VBox vbox = new VBox(focusBtn, listView);
 875 
 876         StageLoader sl = new StageLoader(vbox);
 877         sl.getStage().requestFocus();
 878         focusBtn.requestFocus();
 879         Toolkit.getToolkit().firePulse();
 880 
 881         // test initial state
 882         assertEquals(sl.getStage().getScene().getFocusOwner(), focusBtn);
 883         assertTrue(focusBtn.isFocused());
 884         assertEquals(-1, sm.getSelectedIndex());
 885         assertNull(sm.getSelectedItem());
 886 
 887         // move focus to the listview
 888         listView.requestFocus();
 889 
 890         // ensure that there is a selection (where previously there was not one)
 891         assertEquals(sl.getStage().getScene().getFocusOwner(), listView);
 892         assertTrue(listView.isFocused());
 893         assertEquals(-1, sm.getSelectedIndex());
 894         assertNull(sm.getSelectedItem());
 895 
 896         sl.dispose();
 897     }
 898 
 899     private int rt_37061_index_counter = 0;
 900     private int rt_37061_item_counter = 0;
 901     @Test public void test_rt_37061() {
 902         ListView<Integer> tv = new ListView<>();
 903         tv.getItems().add(1);
 904         tv.getSelectionModel().select(0);
 905 
 906         // note we add the listeners after the selection is made, so the counters
 907         // at this point are still both at zero.
 908         tv.getSelectionModel().selectedIndexProperty().addListener((observable, oldValue, newValue) -> {
 909             rt_37061_index_counter++;
 910         });
 911 
 912         tv.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
 913             rt_37061_item_counter++;
 914         });
 915 
 916         // add a new item. This does not impact the selected index or selected item
 917         // so the counters should remain at zero.
 918         tv.getItems().add(2);
 919         assertEquals(0, rt_37061_index_counter);
 920         assertEquals(0, rt_37061_item_counter);
 921     }
 922 
 923     private int rt_37538_count = 0;
 924     @Test public void test_rt_37538_noCNextCall() {
 925         test_rt_37538(false, false);
 926     }
 927 
 928     @Test public void test_rt_37538_callCNextOnce() {
 929         test_rt_37538(true, false);
 930     }
 931 
 932     @Test public void test_rt_37538_callCNextInLoop() {
 933         test_rt_37538(false, true);
 934     }
 935 
 936     private void test_rt_37538(boolean callCNextOnce, boolean callCNextInLoop) {
 937         ListView<Integer> list = new ListView<>();
 938         for ( int i = 1; i <= 50; i++ ) {
 939             list.getItems().add(i);
 940         }
 941 
 942         list.getSelectionModel().getSelectedItems().addListener((ListChangeListener.Change<? extends Integer> c) -> {
 943             if (callCNextOnce) {
 944                 c.next();
 945             } else if (callCNextInLoop) {
 946                 while (c.next()) {
 947                     // no-op
 948                 }
 949             }
 950 
 951             if (rt_37538_count >= 1) {
 952                 Thread.dumpStack();
 953                 fail("This method should only be called once");
 954             }
 955 
 956             rt_37538_count++;
 957         });
 958 
 959         StageLoader sl = new StageLoader(list);
 960         assertEquals(0, rt_37538_count);
 961         list.getSelectionModel().select(0);
 962         assertEquals(1, rt_37538_count);
 963         sl.dispose();
 964     }
 965 
 966     @Test
 967     public void test_rt_35395_fixedCellSize() {
 968         test_rt_35395(true);
 969     }
 970 
 971     @Test
 972     public void test_rt_35395_notFixedCellSize() {
 973         test_rt_35395(false);
 974     }
 975 
 976     private int rt_35395_counter;
 977 
 978     private void test_rt_35395(boolean useFixedCellSize) {
 979         rt_35395_counter = 0;
 980 
 981         ObservableList<String> items = FXCollections.observableArrayList();
 982         for (int i = 0; i < 20; ++i) {
 983             items.addAll("red", "green", "blue", "purple");
 984         }
 985 
 986         ListView<String> listView = new ListView<>(items);
 987         if (useFixedCellSize) {
 988             listView.setFixedCellSize(24);
 989         }
 990         listView.setCellFactory(lv -> new ListCellShim<String>() {
 991             @Override
 992             public void updateItem(String color, boolean empty) {
 993                 rt_35395_counter += 1;
 994                 super.updateItem(color, empty);
 995                 setText(null);
 996                 if (empty) {
 997                     setGraphic(null);
 998                 } else {
 999                     Rectangle rect = new Rectangle(16, 16);
1000                     rect.setStyle("-fx-fill: " + color);
1001                     setGraphic(rect);
1002                 }
1003             }
1004         });
1005 
1006         StageLoader sl = new StageLoader(listView);
1007 
1008         Platform.runLater(() -> {
1009             rt_35395_counter = 0;
1010             items.set(10, "yellow");
1011             Platform.runLater(() -> {
1012                 Toolkit.getToolkit().firePulse();
1013                 assertEquals(1, rt_35395_counter);
1014                 rt_35395_counter = 0;
1015                 items.set(30, "yellow");
1016                 Platform.runLater(() -> {
1017                     Toolkit.getToolkit().firePulse();
1018                     assertEquals(0, rt_35395_counter);
1019                     rt_35395_counter = 0;
1020                     items.remove(12);
1021                     Platform.runLater(() -> {
1022                         Toolkit.getToolkit().firePulse();
1023                         assertEquals(useFixedCellSize ? 39 : 45, rt_35395_counter);
1024                         rt_35395_counter = 0;
1025                         items.add(12, "yellow");
1026                         Platform.runLater(() -> {
1027                             Toolkit.getToolkit().firePulse();
1028                             assertEquals(useFixedCellSize ? 39 : 45, rt_35395_counter);
1029                             rt_35395_counter = 0;
1030                             listView.scrollTo(5);
1031                             Platform.runLater(() -> {
1032                                 Toolkit.getToolkit().firePulse();
1033                                 assertEquals(5, rt_35395_counter);
1034                                 rt_35395_counter = 0;
1035                                 listView.scrollTo(55);
1036                                 Platform.runLater(() -> {
1037                                     Toolkit.getToolkit().firePulse();
1038                                     assertEquals(useFixedCellSize ? 17 : 53, rt_35395_counter);
1039                                     sl.dispose();
1040                                 });
1041                             });
1042                         });
1043                     });
1044                 });
1045             });
1046         });
1047     }
1048 
1049     @Test public void test_rt_37632() {
1050         final ObservableList<String> listOne = FXCollections.observableArrayList("A", "B", "C");
1051         final ObservableList<String> listTwo = FXCollections.observableArrayList("C");
1052 
1053         final ListView<String> listView = new ListView<>();
1054         MultipleSelectionModel<String> sm = listView.getSelectionModel();
1055         listView.setItems(listOne);
1056         listView.getSelectionModel().selectFirst();
1057 
1058         assertEquals(0, sm.getSelectedIndex());
1059         assertEquals("A", sm.getSelectedItem());
1060         assertEquals(1, sm.getSelectedIndices().size());
1061         assertEquals(0, (int) sm.getSelectedIndices().get(0));
1062         assertEquals(1, sm.getSelectedItems().size());
1063         assertEquals("A", sm.getSelectedItems().get(0));
1064 
1065         listView.setItems(listTwo);
1066 
1067         assertEquals(-1, sm.getSelectedIndex());
1068         assertNull(sm.getSelectedItem());
1069         assertEquals(0, sm.getSelectedIndices().size());
1070         assertEquals(0, sm.getSelectedItems().size());
1071     }
1072 
1073     private int rt_37853_cancelCount;
1074     private int rt_37853_commitCount;
1075     @Test public void test_rt_37853() {
1076         listView.setCellFactory(TextFieldListCell.forListView());
1077         listView.setEditable(true);
1078 
1079         for (int i = 0; i < 10; i++) {
1080             listView.getItems().add("" + i);
1081         }
1082 
1083         StageLoader sl = new StageLoader(listView);
1084 
1085         listView.setOnEditCancel(editEvent -> rt_37853_cancelCount++);
1086         listView.setOnEditCommit(editEvent -> rt_37853_commitCount++);
1087 
1088         assertEquals(0, rt_37853_cancelCount);
1089         assertEquals(0, rt_37853_commitCount);
1090 
1091         listView.edit(1);
1092         assertNotNull(listView.getEditingIndex());
1093 
1094         listView.getItems().clear();
1095         assertEquals(1, rt_37853_cancelCount);
1096         assertEquals(0, rt_37853_commitCount);
1097 
1098         sl.dispose();
1099     }
1100 
1101     @Test public void test_rt_38787_remove_b() {
1102         // selection moves to "a"
1103         test_rt_38787("a", 0, "b");
1104     }
1105 
1106     @Test public void test_rt_38787_remove_b_c() {
1107         // selection moves to "a"
1108         test_rt_38787("a", 0, "b", "c");
1109     }
1110 
1111     @Test public void test_rt_38787_remove_c_d() {
1112         // selection moves to "b"
1113         test_rt_38787("b", 1, "c", "d");
1114     }
1115 
1116     @Test public void test_rt_38787_remove_a() {
1117         // selection moves to "b", now in index 0
1118         test_rt_38787("b", 0, "a");
1119     }
1120 
1121     @Test public void test_rt_38787_remove_z() {
1122         // selection shouldn't move as 'z' doesn't exist
1123         test_rt_38787("b", 1, "z");
1124     }
1125 
1126     private void test_rt_38787(String expectedItem, int expectedIndex, String... itemsToRemove) {
1127         ListView<String> stringListView = new ListView<>();
1128         stringListView.getItems().addAll("a","b","c","d");
1129 
1130         MultipleSelectionModel<String> sm = stringListView.getSelectionModel();
1131         sm.select("b");
1132 
1133         // test pre-conditions
1134         assertEquals(1, sm.getSelectedIndex());
1135         assertEquals(1, (int)sm.getSelectedIndices().get(0));
1136         assertEquals("b", sm.getSelectedItem());
1137         assertEquals("b", sm.getSelectedItems().get(0));
1138         assertFalse(sm.isSelected(0));
1139         assertTrue(sm.isSelected(1));
1140         assertFalse(sm.isSelected(2));
1141 
1142         // removing items
1143         stringListView.getItems().removeAll(itemsToRemove);
1144 
1145         // testing against expectations
1146         assertEquals(expectedIndex, sm.getSelectedIndex());
1147         assertEquals(expectedIndex, (int)sm.getSelectedIndices().get(0));
1148         assertEquals(expectedItem, sm.getSelectedItem());
1149         assertEquals(expectedItem, sm.getSelectedItems().get(0));
1150     }
1151 
1152     private int rt_38341_indices_count = 0;
1153     private int rt_38341_items_count = 0;
1154     @Test public void test_rt_38341() {
1155         ListView<String> stringListView = new ListView<>();
1156         stringListView.getItems().addAll("a","b","c","d");
1157 
1158         MultipleSelectionModel<String> sm = stringListView.getSelectionModel();
1159         sm.getSelectedIndices().addListener((ListChangeListener<Integer>) c -> rt_38341_indices_count++);
1160         sm.getSelectedItems().addListener((ListChangeListener<String>) c -> rt_38341_items_count++);
1161 
1162         assertEquals(0, rt_38341_indices_count);
1163         assertEquals(0, rt_38341_items_count);
1164 
1165         // expand the first child of root, and select it (note: root isn't visible)
1166         sm.select(1);
1167         assertEquals(1, sm.getSelectedIndex());
1168         assertEquals(1, sm.getSelectedIndices().size());
1169         assertEquals(1, (int)sm.getSelectedIndices().get(0));
1170         assertEquals(1, sm.getSelectedItems().size());
1171         assertEquals("b", sm.getSelectedItem());
1172         assertEquals("b", sm.getSelectedItems().get(0));
1173 
1174         assertEquals(1, rt_38341_indices_count);
1175         assertEquals(1, rt_38341_items_count);
1176 
1177         // now delete it
1178         stringListView.getItems().remove(1);
1179 
1180         // selection should move to the childs parent in index 0
1181         assertEquals(0, sm.getSelectedIndex());
1182         assertEquals(1, sm.getSelectedIndices().size());
1183         assertEquals(0, (int)sm.getSelectedIndices().get(0));
1184         assertEquals(1, sm.getSelectedItems().size());
1185         assertEquals("a", sm.getSelectedItem());
1186         assertEquals("a", sm.getSelectedItems().get(0));
1187 
1188         // we also expect there to be an event in the selection model for
1189         // selected indices and selected items
1190         assertEquals(sm.getSelectedIndices() +"", 2, rt_38341_indices_count);
1191         assertEquals(2, rt_38341_items_count);
1192     }
1193 
1194     @Test public void test_rt_39132() {
1195         ObservableList items = FXCollections.observableArrayList("one", "two", "three");
1196         ListView listView = new ListView<>();
1197         listView.setItems(items);
1198 
1199         MultipleSelectionModel sm = listView.getSelectionModel();
1200         sm.select(0);
1201 
1202         assertEquals(0, sm.getSelectedIndex());
1203         assertEquals("one", sm.getSelectedItem());
1204 
1205         items.add(0, "new item");
1206         assertEquals(1, sm.getSelectedIndex());
1207         assertEquals("one", sm.getSelectedItem());
1208     }
1209 
1210     private int rt_38943_index_count = 0;
1211     private int rt_38943_item_count = 0;
1212     @Test public void test_rt_38943() {
1213         ListView<String> listView = new ListView<>(FXCollections.observableArrayList("one", "two", "three"));
1214 
1215         MultipleSelectionModel sm = listView.getSelectionModel();
1216 
1217         sm.selectedIndexProperty().addListener((observable, oldValue, newValue) -> rt_38943_index_count++);
1218         sm.selectedItemProperty().addListener((observable, oldValue, newValue) -> rt_38943_item_count++);
1219 
1220         assertEquals(-1, sm.getSelectedIndex());
1221         assertNull(sm.getSelectedItem());
1222         assertEquals(0, rt_38943_index_count);
1223         assertEquals(0, rt_38943_item_count);
1224 
1225         sm.select(0);
1226         assertEquals(0, sm.getSelectedIndex());
1227         assertEquals("one", sm.getSelectedItem());
1228         assertEquals(1, rt_38943_index_count);
1229         assertEquals(1, rt_38943_item_count);
1230 
1231         sm.clearSelection(0);
1232         assertEquals(-1, sm.getSelectedIndex());
1233         assertNull(sm.getSelectedItem());
1234         assertEquals(2, rt_38943_index_count);
1235         assertEquals(2, rt_38943_item_count);
1236     }
1237 
1238     @Test public void test_rt_38884() {
1239         ListView<String> listView = new ListView<>();
1240         ObservableList<String> items = listView.getItems();
1241 
1242         listView.getSelectionModel().getSelectedItems().addListener((ListChangeListener.Change<? extends String> c) -> {
1243             while (c.next()) {
1244                 if (c.wasRemoved()) {
1245                     assertTrue(c.getRemovedSize() > 0);
1246 
1247                     List<? extends String> removed = c.getRemoved();
1248                     String removedItem = null;
1249                     try {
1250                         removedItem = removed.get(0);
1251                     } catch (Exception e) {
1252                         fail();
1253                     }
1254 
1255                     assertEquals("foo", removedItem);
1256                 }
1257             }
1258         });
1259 
1260         items.add("foo");
1261         listView.getSelectionModel().select(0);
1262         items.clear();
1263     }
1264 
1265     private int rt_37360_add_count = 0;
1266     private int rt_37360_remove_count = 0;
1267     @Test public void test_rt_37360() {
1268         ListView<String> stringListView = new ListView<>();
1269         stringListView.getItems().addAll("a", "b");
1270 
1271         MultipleSelectionModel<String> sm = stringListView.getSelectionModel();
1272         sm.setSelectionMode(SelectionMode.MULTIPLE);
1273         sm.getSelectedItems().addListener((ListChangeListener<String>) c -> {
1274             while (c.next()) {
1275                 if (c.wasAdded()) {
1276                     rt_37360_add_count += c.getAddedSize();
1277                 }
1278                 if (c.wasRemoved()) {
1279                     rt_37360_remove_count += c.getRemovedSize();
1280                 }
1281             }
1282         });
1283 
1284         assertEquals(0, sm.getSelectedItems().size());
1285         assertEquals(0, rt_37360_add_count);
1286         assertEquals(0, rt_37360_remove_count);
1287 
1288         sm.select(0);
1289         assertEquals(1, sm.getSelectedItems().size());
1290         assertEquals(1, rt_37360_add_count);
1291         assertEquals(0, rt_37360_remove_count);
1292 
1293         sm.select(1);
1294         assertEquals(2, sm.getSelectedItems().size());
1295         assertEquals(2, rt_37360_add_count);
1296         assertEquals(0, rt_37360_remove_count);
1297 
1298         sm.clearAndSelect(1);
1299         assertEquals(1, sm.getSelectedItems().size());
1300         assertEquals(2, rt_37360_add_count);
1301         assertEquals(1, rt_37360_remove_count);
1302     }
1303 
1304     @Test public void test_rt_38491() {
1305         ListView<String> stringListView = new ListView<>();
1306         stringListView.getItems().addAll("a", "b");
1307 
1308         MultipleSelectionModel<String> sm = stringListView.getSelectionModel();
1309         sm.setSelectionMode(SelectionMode.MULTIPLE);
1310 
1311         FocusModel<String> fm = stringListView.getFocusModel();
1312 
1313         // click on row 0
1314         VirtualFlowTestUtils.clickOnRow(stringListView, 0);
1315         assertTrue(sm.isSelected(0));
1316         assertEquals("a", sm.getSelectedItem());
1317         assertTrue(fm.isFocused(0));
1318         assertEquals("a", fm.getFocusedItem());
1319         assertEquals(0, fm.getFocusedIndex());
1320 
1321         int anchor = ListCellBehavior.getAnchor(stringListView, -1);
1322         assertTrue(ListCellBehavior.hasNonDefaultAnchor(stringListView));
1323         assertEquals(0, anchor);
1324 
1325         // now add a new item at row 0. This has the effect of pushing down
1326         // the selected item into row 1.
1327         stringListView.getItems().add(0, "z");
1328 
1329         // The first bug was that selection and focus were not moving down to
1330         // be on row 1, so we test that now
1331         assertFalse(sm.isSelected(0));
1332         assertFalse(fm.isFocused(0));
1333         assertTrue(sm.isSelected(1));
1334         assertEquals("a", sm.getSelectedItem());
1335         assertTrue(fm.isFocused(1));
1336         assertEquals("a", fm.getFocusedItem());
1337         assertEquals(1, fm.getFocusedIndex());
1338 
1339         // The second bug was that the anchor was not being pushed down as well
1340         // (when it should).
1341         anchor = ListCellBehavior.getAnchor(stringListView, -1);
1342         assertTrue(ListCellBehavior.hasNonDefaultAnchor(stringListView));
1343         assertEquals(1, anchor);
1344     }
1345 
1346     private final ObservableList<String> rt_39256_list = FXCollections.observableArrayList();
1347     @Test public void test_rt_39256() {
1348         ListView<String> stringListView = new ListView<>();
1349         stringListView.getItems().addAll("a","b", "c", "d");
1350 
1351         MultipleSelectionModel<String> sm = stringListView.getSelectionModel();
1352         sm.setSelectionMode(SelectionMode.MULTIPLE);
1353 
1354 //        rt_39256_list.addListener((ListChangeListener<String>) change -> {
1355 //            while (change.next()) {
1356 //                System.err.println("number of selected persons (in bound list): " + change.getList().size());
1357 //            }
1358 //        });
1359 
1360         Bindings.bindContent(rt_39256_list, sm.getSelectedItems());
1361 
1362         assertEquals(0, sm.getSelectedItems().size());
1363         assertEquals(0, rt_39256_list.size());
1364 
1365         sm.selectAll();
1366         assertEquals(4, sm.getSelectedItems().size());
1367         assertEquals(4, rt_39256_list.size());
1368 
1369         sm.selectAll();
1370         assertEquals(4, sm.getSelectedItems().size());
1371         assertEquals(4, rt_39256_list.size());
1372 
1373         sm.selectAll();
1374         assertEquals(4, sm.getSelectedItems().size());
1375         assertEquals(4, rt_39256_list.size());
1376     }
1377 
1378     private final ObservableList<String> rt_39482_list = FXCollections.observableArrayList();
1379     @Test public void test_rt_39482() {
1380         ListView<String> stringListView = new ListView<>();
1381         stringListView.getItems().addAll("a", "b", "c", "d");
1382 
1383         MultipleSelectionModel<String> sm = stringListView.getSelectionModel();
1384         sm.setSelectionMode(SelectionMode.MULTIPLE);
1385 
1386         sm.getSelectedItems().addListener((ListChangeListener<String>) change -> {
1387             while (change.next()) {
1388                 System.out.println("sm.getSelectedItems(): " + change.getList());
1389             }
1390         });
1391 
1392         rt_39482_list.addListener((ListChangeListener<String>) change -> {
1393             while (change.next()) {
1394                 System.out.println("rt_39482_list: " + change.getList());
1395             }
1396         });
1397 
1398         Bindings.bindContent(rt_39482_list, sm.getSelectedItems());
1399 
1400         assertEquals(0, sm.getSelectedItems().size());
1401         assertEquals(0, rt_39482_list.size());
1402 
1403         test_rt_39482_selectRow("a", sm, 0);
1404         test_rt_39482_selectRow("b", sm, 1);
1405         test_rt_39482_selectRow("c", sm, 2);
1406         test_rt_39482_selectRow("d", sm, 3);
1407     }
1408 
1409     private void test_rt_39482_selectRow(String expectedString,
1410                                          MultipleSelectionModel<String> sm,
1411                                          int rowToSelect) {
1412         System.out.println("\nSelect row " + rowToSelect);
1413         sm.selectAll();
1414         assertEquals(4, sm.getSelectedIndices().size());
1415         assertEquals(4, sm.getSelectedItems().size());
1416         assertEquals(4, rt_39482_list.size());
1417 
1418         sm.clearAndSelect(rowToSelect);
1419         assertEquals(1, sm.getSelectedIndices().size());
1420         assertEquals(1, sm.getSelectedItems().size());
1421         assertEquals(expectedString, sm.getSelectedItem());
1422         assertEquals(expectedString, rt_39482_list.get(0));
1423         assertEquals(1, rt_39482_list.size());
1424     }
1425 
1426     @Test public void test_rt_39559_useSM_selectAll() {
1427         test_rt_39559(true);
1428     }
1429 
1430     @Test public void test_rt_39559_useKeyboard_selectAll() {
1431         test_rt_39559(false);
1432     }
1433 
1434     private void test_rt_39559(boolean useSMSelectAll) {
1435         ListView<String> stringListView = new ListView<>();
1436         stringListView.getItems().addAll("a", "b", "c", "d");
1437 
1438         MultipleSelectionModel<String> sm = stringListView.getSelectionModel();
1439         sm.setSelectionMode(SelectionMode.MULTIPLE);
1440 
1441         StageLoader sl = new StageLoader(stringListView);
1442         KeyEventFirer keyboard = new KeyEventFirer(stringListView);
1443 
1444         assertEquals(0, sm.getSelectedItems().size());
1445 
1446         sm.clearAndSelect(0);
1447 
1448         if (useSMSelectAll) {
1449             sm.selectAll();
1450         } else {
1451             keyboard.doKeyPress(KeyCode.A, KeyModifier.getShortcutKey());
1452         }
1453 
1454         assertEquals(4, sm.getSelectedItems().size());
1455         assertEquals(0, (int) ListCellBehavior.getAnchor(stringListView, -1));
1456 
1457         keyboard.doKeyPress(KeyCode.DOWN, KeyModifier.SHIFT);
1458 
1459         assertEquals(0, (int) ListCellBehavior.getAnchor(stringListView, -1));
1460         assertEquals(2, sm.getSelectedItems().size());
1461         assertEquals("a", sm.getSelectedItems().get(0));
1462         assertEquals("b", sm.getSelectedItems().get(1));
1463 
1464         sl.dispose();
1465     }
1466 
1467     @Test public void test_rt_16068_firstElement_selectAndRemoveSameRow() {
1468         // select and then remove the 'a' item, selection and focus should both
1469         // stay at the first row, now 'b'
1470         test_rt_16068(0, 0, 0);
1471     }
1472 
1473     @Test public void test_rt_16068_firstElement_selectRowAndRemoveLaterSibling() {
1474         // select row 'a', and remove row 'c', selection and focus should not change
1475         test_rt_16068(0, 2, 0);
1476     }
1477 
1478     @Test public void test_rt_16068_middleElement_selectAndRemoveSameRow() {
1479         // select and then remove the 'b' item, selection and focus should both
1480         // move up one row to the 'a' item
1481         test_rt_16068(1, 1, 0);
1482     }
1483 
1484     @Test public void test_rt_16068_middleElement_selectRowAndRemoveLaterSibling() {
1485         // select row 'b', and remove row 'c', selection and focus should not change
1486         test_rt_16068(1, 2, 1);
1487     }
1488 
1489     @Test public void test_rt_16068_middleElement_selectRowAndRemoveEarlierSibling() {
1490         // select row 'b', and remove row 'a', selection and focus should move up
1491         // one row, remaining on 'b'
1492         test_rt_16068(1, 0, 0);
1493     }
1494 
1495     @Test public void test_rt_16068_lastElement_selectAndRemoveSameRow() {
1496         // select and then remove the 'd' item, selection and focus should both
1497         // move up one row to the 'c' item
1498         test_rt_16068(3, 3, 2);
1499     }
1500 
1501     @Test public void test_rt_16068_lastElement_selectRowAndRemoveEarlierSibling() {
1502         // select row 'd', and remove row 'a', selection and focus should move up
1503         // one row, remaining on 'd'
1504         test_rt_16068(3, 0, 2);
1505     }
1506 
1507     private void test_rt_16068(int indexToSelect, int indexToRemove, int expectedIndex) {
1508         ListView<String> stringListView = new ListView<>();
1509         stringListView.getItems().addAll("a", "b", "c", "d");
1510 
1511         MultipleSelectionModel<?> sm = stringListView.getSelectionModel();
1512         FocusModel<?> fm = stringListView.getFocusModel();
1513 
1514         sm.select(indexToSelect);
1515         assertEquals(indexToSelect, sm.getSelectedIndex());
1516         assertEquals(stringListView.getItems().get(indexToSelect), sm.getSelectedItem());
1517         assertEquals(indexToSelect, fm.getFocusedIndex());
1518         assertEquals(stringListView.getItems().get(indexToSelect), fm.getFocusedItem());
1519 
1520         stringListView.getItems().remove(indexToRemove);
1521         assertEquals(expectedIndex, sm.getSelectedIndex());
1522         assertEquals(stringListView.getItems().get(expectedIndex), sm.getSelectedItem());
1523         assertEquals(expectedIndex, fm.getFocusedIndex());
1524         assertEquals(stringListView.getItems().get(expectedIndex), fm.getFocusedItem());
1525     }
1526 
1527     @Test public void test_rt_22599() {
1528         ObservableList<RT22599_DataType> initialData = FXCollections.observableArrayList(
1529                 new RT22599_DataType(1, "row1"),
1530                 new RT22599_DataType(2, "row2"),
1531                 new RT22599_DataType(3, "row3")
1532         );
1533 
1534         ListView<RT22599_DataType> listView = new ListView<>();
1535         listView.setItems(initialData);
1536 
1537         StageLoader sl = new StageLoader(listView);
1538 
1539         // testing initial state
1540         assertNotNull(listView.getSkin());
1541         assertEquals("row1", VirtualFlowTestUtils.getCell(listView, 0).getText());
1542         assertEquals("row2", VirtualFlowTestUtils.getCell(listView, 1).getText());
1543         assertEquals("row3", VirtualFlowTestUtils.getCell(listView, 2).getText());
1544 
1545         // change row 0 (where "row1" currently resides), keeping same id.
1546         // Because 'set' is called, the control should update to the new content
1547         // without any user interaction
1548         RT22599_DataType data;
1549         initialData.set(0, data = new RT22599_DataType(0, "row1a"));
1550         Toolkit.getToolkit().firePulse();
1551         assertEquals("row1a", VirtualFlowTestUtils.getCell(listView, 0).getText());
1552 
1553         // change the row 0 (where we currently have "row1a") value directly.
1554         // Because there is no associated property, this won't be observed, so
1555         // the control should still show "row1a" rather than "row1b"
1556         data.text = "row1b";
1557         Toolkit.getToolkit().firePulse();
1558         assertEquals("row1a", VirtualFlowTestUtils.getCell(listView, 0).getText());
1559 
1560         // call refresh() to force a refresh of all visible cells
1561         listView.refresh();
1562         Toolkit.getToolkit().firePulse();
1563         assertEquals("row1b", VirtualFlowTestUtils.getCell(listView, 0).getText());
1564 
1565         sl.dispose();
1566     }
1567 
1568     private static class RT22599_DataType {
1569         public int id = 0;
1570         public String text = "";
1571 
1572         public RT22599_DataType(int id, String text) {
1573             this.id = id;
1574             this.text = text;
1575         }
1576 
1577         @Override public String toString() {
1578             return text;
1579         }
1580 
1581         @Override public boolean equals(Object obj) {
1582             if (obj == null) return false;
1583             return id == ((RT22599_DataType)obj).id;
1584         }
1585     }
1586 
1587     private int rt_39966_count = 0;
1588     @Test public void test_rt_39966() {
1589         ObservableList<String> list = FXCollections.observableArrayList("Hello World");
1590         ListView<String> listView = new ListView<>(list);
1591 
1592         StageLoader sl = new StageLoader(listView);
1593 
1594         // initially there is no selection
1595         assertTrue(listView.getSelectionModel().isEmpty());
1596 
1597         listView.getSelectionModel().selectedItemProperty().addListener((value, s1, s2) -> {
1598             if (rt_39966_count == 0) {
1599                 rt_39966_count++;
1600                 assertFalse(listView.getSelectionModel().isEmpty());
1601             } else {
1602                 assertTrue(listView.getSelectionModel().isEmpty());
1603             }
1604         });
1605 
1606         // our assertion two lines down always succeeds. What fails is our
1607         // assertion above within the listener.
1608         listView.getSelectionModel().select(0);
1609         assertFalse(listView.getSelectionModel().isEmpty());
1610 
1611         list.remove(0);
1612         assertTrue(listView.getSelectionModel().isEmpty());
1613 
1614         sl.dispose();
1615     }
1616 
1617     /**
1618      * Bullet 1: selected index must be updated
1619      * Corner case: last selected. Fails for core
1620      */
1621     @Test public void test_rt_40012_selectedAtLastOnDisjointRemoveItemsAbove() {
1622         ObservableList<String> items = FXCollections.observableArrayList("0", "1", "2", "3", "4", "5");
1623         ListView<String> listView = new ListView<>(items);
1624         SelectionModel sm = listView.getSelectionModel();
1625 
1626         int last = items.size() - 1;
1627 
1628         // selecting item "5"
1629         sm.select(last);
1630 
1631         // disjoint remove of 2 elements above the last selected
1632         // Removing "1" and "3"
1633         items.removeAll(items.get(1), items.get(3));
1634 
1635         // selection should move up two places such that it remains on item "5",
1636         // but in index (last - 2).
1637         int expected = last - 2;
1638         assertEquals("5", sm.getSelectedItem());
1639         assertEquals("selected index after disjoint removes above", expected, sm.getSelectedIndex());
1640     }
1641 
1642     /**
1643      * Variant of 1: if selectedIndex is not updated,
1644      * the old index is no longer valid
1645      * for accessing the items.
1646      */
1647     @Test public void test_rt_40012_accessSelectedAtLastOnDisjointRemoveItemsAbove() {
1648         ObservableList<String> items = FXCollections.observableArrayList("0", "1", "2", "3", "4", "5");
1649         ListView<String> listView = new ListView<>(items);
1650         SelectionModel sm = listView.getSelectionModel();
1651 
1652         int last = items.size() - 1;
1653 
1654         // selecting item "5"
1655         sm.select(last);
1656 
1657         // disjoint remove of 2 elements above the last selected
1658         items.removeAll(items.get(1), items.get(3));
1659         int selected = sm.getSelectedIndex();
1660         if (selected > -1) {
1661             items.get(selected);
1662         }
1663     }
1664 
1665     /**
1666      * Bullet 2: selectedIndex notification count
1667      *
1668      * Note that we don't use the corner case of having the last index selected
1669      * (which fails already on updating the index)
1670      */
1671     private int rt_40012_count = 0;
1672     @Test public void test_rt_40012_selectedIndexNotificationOnDisjointRemovesAbove() {
1673         ObservableList<String> items = FXCollections.observableArrayList("0", "1", "2", "3", "4", "5");
1674         ListView<String> listView = new ListView<>(items);
1675         SelectionModel sm = listView.getSelectionModel();
1676 
1677         int last = items.size() - 2;
1678         sm.select(last);
1679         assertEquals(last, sm.getSelectedIndex());
1680 
1681         rt_40012_count = 0;
1682         sm.selectedIndexProperty().addListener(o -> rt_40012_count++);
1683 
1684         // disjoint remove of 2 elements above the last selected
1685         items.removeAll(items.get(1), items.get(3));
1686         assertEquals("sanity: selectedIndex must be shifted by -2", last - 2, sm.getSelectedIndex());
1687         assertEquals("must fire single event on removes above", 1, rt_40012_count);
1688     }
1689 
1690     /**
1691      * Bullet 3: unchanged selectedItem must not fire change
1692      */
1693     @Test
1694     public void test_rt_40012_selectedItemNotificationOnDisjointRemovesAbove() {
1695         ObservableList<String> items = FXCollections.observableArrayList("0", "1", "2", "3", "4", "5");
1696         ListView<String> listView = new ListView<>(items);
1697         SelectionModel sm = listView.getSelectionModel();
1698 
1699         int last = items.size() - 2;
1700         Object lastItem = items.get(last);
1701         sm.select(last);
1702         assertEquals(lastItem, sm.getSelectedItem());
1703 
1704         rt_40012_count = 0;
1705         sm.selectedItemProperty().addListener(o -> rt_40012_count++);
1706 
1707         // disjoint remove of 2 elements above the last selected
1708         items.removeAll(items.get(1), items.get(3));
1709         assertEquals("sanity: selectedItem unchanged", lastItem, sm.getSelectedItem());
1710         assertEquals("must not fire on unchanged selected item", 0, rt_40012_count);
1711     }
1712 
1713     @Test public void test_rt_40185() {
1714         final ListView<String> lv = new ListView<>();
1715         final ArrayList<Integer> expected = new ArrayList<>();
1716         Collections.addAll(expected, 1, 2);
1717 
1718         lv.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
1719         lv.getSelectionModel().getSelectedIndices().addListener((ListChangeListener<Integer>) change -> {
1720             while (change.next()) {
1721                 if (change.wasRemoved()) {
1722                     assertEquals(expected, change.getRemoved());
1723                 }
1724             }
1725         });
1726 
1727         lv.getItems().addAll("-0-","-1-","-2-");
1728         lv.getSelectionModel().selectIndices(1, 2);
1729         lv.getSelectionModel().clearSelection();
1730     }
1731 
1732     /**
1733      * ClearAndSelect fires invalid change event if selectedIndex is unchanged.
1734      */
1735     private int rt_40212_count = 0;
1736     @Test public void test_rt_40212() {
1737         final ListView<Integer> lv = new ListView<>();
1738         for (int i = 0; i < 10; i++) {
1739             lv.getItems().add(i);
1740         }
1741 
1742         MultipleSelectionModel<Integer> sm = lv.getSelectionModel();
1743         sm.setSelectionMode(SelectionMode.MULTIPLE);
1744 
1745         sm.selectRange(3, 5);
1746         int selected = sm.getSelectedIndex();
1747 
1748         sm.getSelectedIndices().addListener((ListChangeListener<Integer>) change -> {
1749             assertEquals("sanity: selectedIndex unchanged", selected, sm.getSelectedIndex());
1750             while(change.next()) {
1751                 assertEquals("single event on clearAndSelect already selected", 1, ++rt_40212_count);
1752 
1753                 boolean type = change.wasAdded() || change.wasRemoved() || change.wasPermutated() || change.wasUpdated();
1754                 assertTrue("at least one of the change types must be true", type);
1755             }
1756         });
1757 
1758         sm.clearAndSelect(selected);
1759     }
1760 
1761     @Test public void test_rt_40280() {
1762         final ListView<String> view = new ListView<>();
1763         StageLoader sl = new StageLoader(view);
1764         view.getFocusModel().getFocusedIndex();
1765         sl.dispose();
1766     }
1767 
1768     /**
1769      * Test list change of selectedIndices on setIndices. Fails for core ..
1770      */
1771     @Test public void test_rt_40263() {
1772         final ListView<Integer> lv = new ListView<>();
1773         for (int i = 0; i < 10; i++) {
1774             lv.getItems().add(i);
1775         }
1776 
1777         MultipleSelectionModel<Integer> sm = lv.getSelectionModel();
1778         sm.setSelectionMode(SelectionMode.MULTIPLE);
1779 
1780         int[] indices = new int[]{2, 5, 7};
1781         ListChangeListener<Integer> l = c -> {
1782             // firstly, we expect only one change
1783             int subChanges = 0;
1784             while(c.next()) {
1785                 subChanges++;
1786             }
1787             assertEquals(1, subChanges);
1788 
1789             // secondly, we expect the added size to be three, as that is the
1790             // number of items selected
1791             c.reset();
1792             c.next();
1793             System.out.println("Added items: " + c.getAddedSubList());
1794             assertEquals(indices.length, c.getAddedSize());
1795             assertArrayEquals(indices, c.getAddedSubList().stream().mapToInt(i -> i).toArray());
1796         };
1797         sm.getSelectedIndices().addListener(l);
1798         sm.selectIndices(indices[0], indices);
1799     }
1800 
1801     @Test public void test_jdk8141124() {
1802         ListView<String> listView = new ListView<>();
1803         ObservableList<String> items = FXCollections.observableArrayList();
1804         SortedList<String> sortedItems = new SortedList<>(items);
1805         sortedItems.setComparator(String::compareTo);
1806         listView.setItems(sortedItems);
1807 
1808         MultipleSelectionModel<String> sm = listView.getSelectionModel();
1809 
1810         items.add("2");
1811         listView.getSelectionModel().selectFirst();
1812         assertEquals("2", sm.getSelectedItem());
1813         assertEquals(0, sm.getSelectedIndex());
1814         assertEquals(0, (int) sm.getSelectedIndices().get(0));
1815         assertEquals("2", sm.getSelectedItems().get(0));
1816 
1817         items.addAll("1", "3");
1818         assertEquals("2", sm.getSelectedItem());
1819         assertEquals(1, sm.getSelectedIndex());
1820         assertEquals(1, (int) sm.getSelectedIndices().get(0));
1821         assertEquals("2", sm.getSelectedItems().get(0));
1822     }
1823 
1824     @Test public void test_jdk_8143594() {
1825         MultipleSelectionModel model = listView.getSelectionModel();
1826         model.setSelectionMode(SelectionMode.MULTIPLE);
1827 
1828         listView.getItems().addAll("Apple", "Orange", null);
1829 
1830         model.select(0);
1831         model.clearAndSelect(2);
1832         model.clearAndSelect(0);
1833         model.clearAndSelect(2);
1834     }
1835 
1836     @Test public void test_jdk_8145887_selectedIndices_ListIterator() {
1837         int selectIndices[] = { 4, 7, 9 };
1838         ListView<Integer> lv = new ListView<>();
1839         for (int i = 0; i < 10; ++i) {
1840             lv.getItems().add(i);
1841         }
1842 
1843         MultipleSelectionModel msm = lv.getSelectionModel();
1844         msm.setSelectionMode(SelectionMode.MULTIPLE);
1845         for (int i = 0 ; i < selectIndices.length; ++i) {
1846             msm.select(selectIndices[i]);
1847         }
1848 
1849         ListIterator iter = lv.getSelectionModel().getSelectedIndices().listIterator();
1850 
1851         // Step 1. Initial values
1852         assertEquals(0, iter.nextIndex());
1853         assertEquals(-1, iter.previousIndex());
1854         assertEquals(true, iter.hasNext());
1855         assertEquals(false, iter.hasPrevious());
1856 
1857         // Step 2. Iterate forward.
1858         assertEquals(4, iter.next());
1859         assertEquals(1, iter.nextIndex());
1860         assertEquals(0, iter.previousIndex());
1861         assertEquals(true, iter.hasNext());
1862         assertEquals(true, iter.hasPrevious());
1863 
1864         // Step 3. Iterate forward.
1865         // Values would be at similar state of Step 2.
1866         assertEquals(7, iter.next());
1867 
1868         // Step 4. Iterate forward to Last element.
1869         assertEquals(9, iter.next());
1870         assertEquals(3, iter.nextIndex());
1871         assertEquals(2, iter.previousIndex());
1872         assertEquals(false, iter.hasNext());
1873         assertEquals(true, iter.hasPrevious());
1874 
1875         // Step 5. Verify NoSuchElementException by next()
1876         try {
1877             iter.next();
1878         } catch (Exception e) {
1879             assert(e instanceof NoSuchElementException);
1880         }
1881 
1882         // Step 6. Iterate backward to Last element.
1883         assertEquals(9, iter.previous());
1884         assertEquals(2, iter.nextIndex());
1885         assertEquals(1, iter.previousIndex());
1886         assertEquals(true, iter.hasNext());
1887         assertEquals(true, iter.hasPrevious());
1888 
1889         // Step 7. Iterate forward to Last element.
1890         assertEquals(9, iter.next());
1891         assertEquals(3, iter.nextIndex());
1892         assertEquals(2, iter.previousIndex());
1893         assertEquals(false, iter.hasNext());
1894         assertEquals(true, iter.hasPrevious());
1895 
1896         // Step 8. Iterate forward to last element.
1897         // Values would be at Same state of Step 2.
1898         assertEquals(9, iter.previous());
1899 
1900         // Step 9. Iterate backward.
1901         assertEquals(7, iter.previous());
1902         assertEquals(1, iter.nextIndex());
1903         assertEquals(0, iter.previousIndex());
1904         assertEquals(true, iter.hasNext());
1905         assertEquals(true, iter.hasPrevious());
1906 
1907         // Step 10. Iterate back to first element.
1908         assertEquals(4, iter.previous());
1909         assertEquals(0, iter.nextIndex());
1910         assertEquals(-1, iter.previousIndex());
1911         assertEquals(true, iter.hasNext());
1912         assertEquals(false, iter.hasPrevious());
1913 
1914         // Step 11. Verify NoSuchElementException by previous()
1915         try {
1916             iter.previous();
1917         } catch (Exception e) {
1918             assert(e instanceof NoSuchElementException);
1919         }
1920     }
1921 }