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 javafx.scene.control.skin.ListCellSkin;
  29 import javafx.beans.InvalidationListener;
  30 import javafx.collections.FXCollections;
  31 import javafx.collections.ListChangeListener;
  32 import javafx.collections.ObservableList;
  33 
  34 import javafx.scene.control.FocusModel;
  35 import javafx.scene.control.ListCell;
  36 import javafx.scene.control.ListCellShim;
  37 import javafx.scene.control.ListView;
  38 import javafx.scene.control.MultipleSelectionModel;
  39 import javafx.scene.control.MultipleSelectionModelBaseShim;
  40 import javafx.scene.control.SelectionMode;
  41 import org.junit.Before;
  42 import org.junit.Ignore;
  43 import org.junit.Test;
  44 
  45 import static test.com.sun.javafx.scene.control.infrastructure.ControlTestUtils.*;
  46 import static org.junit.Assert.*;
  47 
  48 /**
  49  */
  50 public class ListCellTest {
  51     private ListCell<String> cell;
  52     private ListView<String> list;
  53     private ObservableList<String> model;
  54 
  55     @Before public void setup() {
  56         cell = new ListCell<String>();
  57         model = FXCollections.observableArrayList("Apples", "Oranges", "Pears");
  58         list = new ListView<String>(model);
  59     }
  60 
  61     /*********************************************************************
  62      * Tests for the constructors                                        *
  63      ********************************************************************/
  64 
  65     @Test public void styleClassIs_list_cell_byDefault() {
  66         assertStyleClassContains(cell, "list-cell");
  67     }
  68 
  69     // The item should be null by default because the index is -1 by default
  70     @Test public void itemIsNullByDefault() {
  71         assertNull(cell.getItem());
  72     }
  73 
  74     /*********************************************************************
  75      * Tests for the listView property                                   *
  76      ********************************************************************/
  77 
  78     @Test public void listViewIsNullByDefault() {
  79         assertNull(cell.getListView());
  80         assertNull(cell.listViewProperty().get());
  81     }
  82 
  83     @Test public void updateListViewUpdatesListView() {
  84         cell.updateListView(list);
  85         assertSame(list, cell.getListView());
  86         assertSame(list, cell.listViewProperty().get());
  87     }
  88 
  89     @Test public void canSetListViewBackToNull() {
  90         cell.updateListView(list);
  91         cell.updateListView(null);
  92         assertNull(cell.getListView());
  93         assertNull(cell.listViewProperty().get());
  94     }
  95 
  96     @Test public void listViewPropertyReturnsCorrectBean() {
  97         assertSame(cell, cell.listViewProperty().getBean());
  98     }
  99 
 100     @Test public void listViewPropertyNameIs_listView() {
 101         assertEquals("listView", cell.listViewProperty().getName());
 102     }
 103 
 104     @Test public void updateListViewWithNullFocusModelResultsInNoException() {
 105         cell.updateListView(list);
 106         list.setFocusModel(null);
 107         cell.updateListView(new ListView());
 108     }
 109 
 110     @Test public void updateListViewWithNullFocusModelResultsInNoException2() {
 111         list.setFocusModel(null);
 112         cell.updateListView(list);
 113         cell.updateListView(new ListView());
 114     }
 115 
 116     @Test public void updateListViewWithNullFocusModelResultsInNoException3() {
 117         cell.updateListView(list);
 118         ListView list2 = new ListView();
 119         list2.setFocusModel(null);
 120         cell.updateListView(list2);
 121     }
 122 
 123     @Test public void updateListViewWithNullSelectionModelResultsInNoException() {
 124         cell.updateListView(list);
 125         list.setSelectionModel(null);
 126         cell.updateListView(new ListView());
 127     }
 128 
 129     @Test public void updateListViewWithNullSelectionModelResultsInNoException2() {
 130         list.setSelectionModel(null);
 131         cell.updateListView(list);
 132         cell.updateListView(new ListView());
 133     }
 134 
 135     @Test public void updateListViewWithNullSelectionModelResultsInNoException3() {
 136         cell.updateListView(list);
 137         ListView list2 = new ListView();
 138         list2.setSelectionModel(null);
 139         cell.updateListView(list2);
 140     }
 141 
 142     @Test public void updateListViewWithNullItemsResultsInNoException() {
 143         cell.updateListView(list);
 144         list.setItems(null);
 145         cell.updateListView(new ListView());
 146     }
 147 
 148     @Test public void updateListViewWithNullItemsResultsInNoException2() {
 149         list.setItems(null);
 150         cell.updateListView(list);
 151         cell.updateListView(new ListView());
 152     }
 153 
 154     @Test public void updateListViewWithNullItemsResultsInNoException3() {
 155         cell.updateListView(list);
 156         ListView list2 = new ListView();
 157         list2.setItems(null);
 158         cell.updateListView(list2);
 159     }
 160 
 161     /*********************************************************************
 162      * Tests for the item property. It should be updated whenever the    *
 163      * index, or listView changes, including the listView's items.       *
 164      ********************************************************************/
 165 
 166     @Test public void itemMatchesIndexWithinListItems() {
 167         cell.updateIndex(0);
 168         cell.updateListView(list);
 169         assertSame(model.get(0), cell.getItem());
 170         cell.updateIndex(1);
 171         assertSame(model.get(1), cell.getItem());
 172     }
 173 
 174     @Test public void itemMatchesIndexWithinListItems2() {
 175         cell.updateListView(list);
 176         cell.updateIndex(0);
 177         assertSame(model.get(0), cell.getItem());
 178         cell.updateIndex(1);
 179         assertSame(model.get(1), cell.getItem());
 180     }
 181 
 182     @Test public void itemIsNullWhenIndexIsOutOfRange() {
 183         cell.updateIndex(50);
 184         cell.updateListView(list);
 185         assertNull(cell.getItem());
 186     }
 187 
 188     @Test public void itemIsNullWhenIndexIsOutOfRange2() {
 189         cell.updateListView(list);
 190         cell.updateIndex(50);
 191         assertNull(cell.getItem());
 192     }
 193 
 194     // Above were the simple tests. Now we check various circumstances
 195     // to make sure the item is updated correctly.
 196 
 197     @Test public void itemIsUpdatedWhenItWasOutOfRangeButUpdatesToListViewItemsMakesItInRange() {
 198         cell.updateIndex(4);
 199         cell.updateListView(list);
 200         model.addAll("Pumpkin", "Lemon");
 201         assertSame(model.get(4), cell.getItem());
 202     }
 203 
 204     @Test public void itemIsUpdatedWhenItWasInRangeButUpdatesToListViewItemsMakesItOutOfRange() {
 205         cell.updateIndex(2);
 206         cell.updateListView(list);
 207         assertSame(model.get(2), cell.getItem());
 208         model.remove(2);
 209         assertNull(cell.getItem());
 210     }
 211 
 212     @Test public void itemIsUpdatedWhenListViewItemsIsUpdated() {
 213         cell.updateIndex(1);
 214         cell.updateListView(list);
 215         assertSame(model.get(1), cell.getItem());
 216         model.set(1, "Lime");
 217         assertEquals("Lime", cell.getItem());
 218     }
 219 
 220     @Test public void itemIsUpdatedWhenListViewItemsHasNewItemInsertedBeforeIndex() {
 221         cell.updateIndex(1);
 222         cell.updateListView(list);
 223         assertSame(model.get(1), cell.getItem());
 224         String previous = model.get(0);
 225         model.add(0, "Lime");
 226         assertEquals(previous, cell.getItem());
 227     }
 228 
 229     @Test public void itemIsUpdatedWhenListViewItemsHasItemRemovedBeforeIndex() {
 230         cell.updateIndex(1);
 231         cell.updateListView(list);
 232         assertSame(model.get(1), cell.getItem());
 233         String other = model.get(2);
 234         model.remove(0);
 235         assertEquals(other, cell.getItem());
 236     }
 237 
 238     @Test public void itemIsUpdatedWhenListViewItemsIsReplaced() {
 239         ObservableList<String> model2 = FXCollections.observableArrayList("Water", "Juice", "Soda");
 240         cell.updateIndex(1);
 241         cell.updateListView(list);
 242         list.setItems(model2);
 243         assertEquals("Juice", cell.getItem());
 244     }
 245 
 246     @Test public void itemIsUpdatedWhenListViewIsReplaced() {
 247         cell.updateIndex(2);
 248         cell.updateListView(list);
 249         ObservableList<String> model2 = FXCollections.observableArrayList("Water", "Juice", "Soda");
 250         ListView<String> listView2 = new ListView<String>(model2);
 251         cell.updateListView(listView2);
 252         assertEquals("Soda", cell.getItem());
 253     }
 254 
 255     @Test public void replaceItemsWithANull() {
 256         cell.updateIndex(0);
 257         cell.updateListView(list);
 258         list.setItems(null);
 259         assertNull(cell.getItem());
 260     }
 261 
 262     @Test public void replaceItemsWithANull_ListenersRemovedFromFormerList() {
 263         cell.updateIndex(0);
 264         cell.updateListView(list);
 265         ListChangeListener listener = getListChangeListener(cell, "weakItemsListener");
 266         assertListenerListContains(model, listener);
 267         list.setItems(null);
 268         assertListenerListDoesNotContain(model, listener);
 269     }
 270 
 271     @Test public void replaceANullItemsWithNotNull() {
 272         cell.updateIndex(0);
 273         cell.updateListView(list);
 274         list.setItems(null);
 275         ObservableList<String> model2 = FXCollections.observableArrayList("Water", "Juice", "Soda");
 276         list.setItems(model2);
 277         assertEquals("Water", cell.getItem());
 278     }
 279 
 280     /*********************************************************************
 281      * Tests for the selection listener                                  *
 282      ********************************************************************/
 283 
 284     @Test public void selectionOnSelectionModelIsReflectedInCells() {
 285         cell.updateListView(list);
 286         cell.updateIndex(0);
 287 
 288         ListCell<String> other = new ListCell<String>();
 289         other.updateListView(list);
 290         other.updateIndex(1);
 291 
 292         list.getSelectionModel().selectFirst();
 293         assertTrue(cell.isSelected());
 294         assertFalse(other.isSelected());
 295     }
 296 
 297     @Test public void changesToSelectionOnSelectionModelAreReflectedInCells() {
 298         cell.updateListView(list);
 299         cell.updateIndex(0);
 300 
 301         ListCell<String> other = new ListCell<String>();
 302         other.updateListView(list);
 303         other.updateIndex(1);
 304 
 305         // Because the ListView is in single selection mode, calling
 306         // selectNext causes a loss of focus for the first cell.
 307         list.getSelectionModel().selectFirst();
 308         list.getSelectionModel().selectNext();
 309         assertFalse(cell.isSelected());
 310         assertTrue(other.isSelected());
 311     }
 312 
 313     @Test public void replacingTheSelectionModelCausesSelectionOnCellsToBeUpdated() {
 314         // Cell is configured to represent row 0, which is selected.
 315         cell.updateListView(list);
 316         cell.updateIndex(0);
 317         list.getSelectionModel().select(0);
 318 
 319         // Other is configured to represent row 1 which is not selected.
 320         ListCell<String> other = new ListCell<String>();
 321         other.updateListView(list);
 322         other.updateIndex(1);
 323 
 324         // The replacement selection model has row 1 selected, not row 0
 325         MultipleSelectionModel<String> selectionModel = new SelectionModelMock();
 326         selectionModel.select(1);
 327 
 328         list.setSelectionModel(selectionModel);
 329         assertFalse(cell.isSelected());
 330         assertTrue(other.isSelected());
 331     }
 332 
 333     @Test public void changesToSelectionOnSelectionModelAreReflectedInCells_MultipleSelection() {
 334         list.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
 335         cell.updateListView(list);
 336         cell.updateIndex(0);
 337 
 338         ListCell<String> other = new ListCell<String>();
 339         other.updateListView(list);
 340         other.updateIndex(1);
 341 
 342         list.getSelectionModel().selectFirst();
 343         list.getSelectionModel().selectNext();
 344         assertTrue(cell.isSelected());
 345         assertTrue(other.isSelected());
 346     }
 347 
 348     @Test public void replacingTheSelectionModelCausesSelectionOnCellsToBeUpdated_MultipleSelection() {
 349         // Cell is configured to represent row 0, which is selected.
 350         cell.updateListView(list);
 351         cell.updateIndex(0);
 352         list.getSelectionModel().select(0);
 353 
 354         // Other is configured to represent row 1 which is not selected.
 355         ListCell<String> other = new ListCell<String>();
 356         other.updateListView(list);
 357         other.updateIndex(1);
 358 
 359         // The replacement selection model has row 0 and 1 selected
 360         MultipleSelectionModel<String> selectionModel = new SelectionModelMock();
 361         selectionModel.setSelectionMode(SelectionMode.MULTIPLE);
 362         selectionModel.selectIndices(0, 1);
 363 
 364         list.setSelectionModel(selectionModel);
 365         assertTrue(cell.isSelected());
 366         assertTrue(other.isSelected());
 367     }
 368 
 369     @Test public void replaceANullSelectionModel() {
 370         // Cell is configured to represent row 0, which is selected.
 371         list.setSelectionModel(null);
 372         cell.updateIndex(0);
 373         cell.updateListView(list);
 374 
 375         // Other is configured to represent row 1 which is not selected.
 376         ListCell<String> other = new ListCell<String>();
 377         other.updateListView(list);
 378         other.updateIndex(1);
 379 
 380         // The replacement selection model has row 1 selected
 381         MultipleSelectionModel<String> selectionModel = new SelectionModelMock();
 382         selectionModel.select(1);
 383 
 384         list.setSelectionModel(selectionModel);
 385         assertFalse(cell.isSelected());
 386         assertTrue(other.isSelected());
 387     }
 388 
 389     @Test public void setANullSelectionModel() {
 390         // Cell is configured to represent row 0, which is selected.
 391         cell.updateIndex(0);
 392         cell.updateListView(list);
 393 
 394         // Other is configured to represent row 1 which is not selected.
 395         ListCell<String> other = new ListCell<String>();
 396         other.updateListView(list);
 397         other.updateIndex(1);
 398 
 399         // Replace with a null selection model, which should clear selection
 400         list.setSelectionModel(null);
 401         assertFalse(cell.isSelected());
 402         assertFalse(other.isSelected());
 403     }
 404 
 405     @Ignore @Test public void replacingTheSelectionModelRemovesTheListenerFromTheOldModel() {
 406         cell.updateIndex(0);
 407         cell.updateListView(list);
 408         MultipleSelectionModel<String> sm = list.getSelectionModel();
 409         ListChangeListener listener = getListChangeListener(cell, "weakSelectedListener");
 410         assertListenerListContains(sm.getSelectedIndices(), listener);
 411         list.setSelectionModel(new SelectionModelMock());
 412         assertListenerListDoesNotContain(sm.getSelectedIndices(), listener);
 413     }
 414 
 415     /*********************************************************************
 416      * Tests for the focus listener                                      *
 417      ********************************************************************/
 418 
 419     @Test public void focusOnFocusModelIsReflectedInCells() {
 420         cell.updateListView(list);
 421         cell.updateIndex(0);
 422 
 423         ListCell<String> other = new ListCell<String>();
 424         other.updateListView(list);
 425         other.updateIndex(1);
 426 
 427         list.getFocusModel().focus(0);
 428         assertTrue(cell.isFocused());
 429         assertFalse(other.isFocused());
 430     }
 431 
 432     @Test public void changesToFocusOnFocusModelAreReflectedInCells() {
 433         cell.updateListView(list);
 434         cell.updateIndex(0);
 435 
 436         ListCell<String> other = new ListCell<String>();
 437         other.updateListView(list);
 438         other.updateIndex(1);
 439 
 440         list.getFocusModel().focus(0);
 441         list.getFocusModel().focus(1);
 442         assertFalse(cell.isFocused());
 443         assertTrue(other.isFocused());
 444     }
 445 
 446     @Test public void replacingTheFocusModelCausesFocusOnCellsToBeUpdated() {
 447         // Cell is configured to represent row 0, which is focused.
 448         cell.updateListView(list);
 449         cell.updateIndex(0);
 450         list.getFocusModel().focus(0);
 451 
 452         // Other is configured to represent row 1 which is not focused.
 453         ListCell<String> other = new ListCell<String>();
 454         other.updateListView(list);
 455         other.updateIndex(1);
 456 
 457         // The replacement focus model has row 1 selected, not row 0
 458         FocusModel<String> focusModel = new FocusModelMock();
 459         focusModel.focus(1);
 460 
 461         list.setFocusModel(focusModel);
 462         assertFalse(cell.isFocused());
 463         assertTrue(other.isFocused());
 464     }
 465 
 466     @Test public void replaceANullFocusModel() {
 467         // Cell is configured to represent row 0, which is focused.
 468         list.setFocusModel(null);
 469         cell.updateIndex(0);
 470         cell.updateListView(list);
 471 
 472         // Other is configured to represent row 1 which is not focused
 473         ListCell<String> other = new ListCell<String>();
 474         other.updateListView(list);
 475         other.updateIndex(1);
 476 
 477         // The replacement focus model has row 1 focused
 478         FocusModel<String> focusModel = new FocusModelMock();
 479         focusModel.focus(1);
 480 
 481         list.setFocusModel(focusModel);
 482         assertFalse(cell.isFocused());
 483         assertTrue(other.isFocused());
 484     }
 485 
 486     @Test public void setANullFocusModel() {
 487         // Cell is configured to represent row 0, which is focused.
 488         cell.updateIndex(0);
 489         cell.updateListView(list);
 490 
 491         // Other is configured to represent row 1 which is not focused.
 492         ListCell<String> other = new ListCell<>();
 493         other.updateListView(list);
 494         other.updateIndex(1);
 495 
 496         // Replace with a null focus model, which should clear focused
 497         list.setFocusModel(null);
 498         assertFalse(cell.isFocused());
 499         assertFalse(other.isFocused());
 500     }
 501 
 502     @Test public void replacingTheFocusModelRemovesTheListenerFromTheOldModel() {
 503         cell.updateIndex(0);
 504         cell.updateListView(list);
 505         FocusModel<String> fm = list.getFocusModel();
 506         InvalidationListener listener = getInvalidationListener(cell, "weakFocusedListener");
 507         assertValueListenersContains(fm.focusedIndexProperty(), listener);
 508         list.setFocusModel(new FocusModelMock());
 509         assertValueListenersDoesNotContain(fm.focusedIndexProperty(), listener);
 510     }
 511 
 512     /*********************************************************************
 513      * Tests for all things related to editing one of these guys         *
 514      ********************************************************************/
 515 
 516     // startEdit()
 517     @Test public void editOnListViewResultsInEditingInCell() {
 518         list.setEditable(true);
 519         cell.updateListView(list);
 520         cell.updateIndex(1);
 521         list.edit(1);
 522         assertTrue(cell.isEditing());
 523     }
 524 
 525     @Test public void editOnListViewResultsInNotEditingInCellWhenDifferentIndex() {
 526         list.setEditable(true);
 527         cell.updateListView(list);
 528         cell.updateIndex(1);
 529         list.edit(0);
 530         assertFalse(cell.isEditing());
 531     }
 532 
 533     @Test public void editCellWithNullListViewResultsInNoExceptions() {
 534         cell.updateIndex(1);
 535         cell.startEdit();
 536     }
 537 
 538     @Test public void editCellOnNonEditableListDoesNothing() {
 539         cell.updateIndex(1);
 540         cell.updateListView(list);
 541         cell.startEdit();
 542         assertFalse(cell.isEditing());
 543         assertEquals(-1, list.getEditingIndex());
 544     }
 545 
 546     @Test public void editCellWithListResultsInUpdatedEditingIndexProperty() {
 547         list.setEditable(true);
 548         cell.updateListView(list);
 549         cell.updateIndex(1);
 550         cell.startEdit();
 551         assertEquals(1, list.getEditingIndex());
 552     }
 553 
 554     @Test public void editCellFiresEventOnList() {
 555         list.setEditable(true);
 556         cell.updateListView(list);
 557         cell.updateIndex(2);
 558         final boolean[] called = new boolean[] { false };
 559         list.setOnEditStart(event -> {
 560             called[0] = true;
 561         });
 562         cell.startEdit();
 563         assertTrue(called[0]);
 564     }
 565 
 566     // commitEdit()
 567     @Test public void commitWhenListIsNullIsOK() {
 568         cell.updateIndex(1);
 569         cell.startEdit();
 570         cell.commitEdit("Watermelon");
 571     }
 572 
 573     @Test public void commitWhenListIsNotNullWillUpdateTheItemsList() {
 574         list.setEditable(true);
 575         cell.updateListView(list);
 576         cell.updateIndex(1);
 577         cell.startEdit();
 578         cell.commitEdit("Watermelon");
 579         assertEquals("Watermelon", list.getItems().get(1));
 580     }
 581 
 582     @Test public void commitSendsEventToList() {
 583         list.setEditable(true);
 584         cell.updateListView(list);
 585         cell.updateIndex(1);
 586         cell.startEdit();
 587         final boolean[] called = new boolean[] { false };
 588         list.setOnEditCommit(event -> {
 589             called[0] = true;
 590         });
 591         cell.commitEdit("Watermelon");
 592         assertTrue(called[0]);
 593     }
 594 
 595     @Test public void afterCommitListViewEditingIndexIsNegativeOne() {
 596         list.setEditable(true);
 597         cell.updateListView(list);
 598         cell.updateIndex(1);
 599         cell.startEdit();
 600         cell.commitEdit("Watermelon");
 601         assertEquals(-1, list.getEditingIndex());
 602         assertFalse(cell.isEditing());
 603     }
 604 
 605     // cancelEdit()
 606     @Test public void cancelEditCanBeCalledWhileListViewIsNull() {
 607         cell.updateIndex(1);
 608         cell.startEdit();
 609         cell.cancelEdit();
 610     }
 611 
 612     @Test public void cancelEditFiresChangeEvent() {
 613         list.setEditable(true);
 614         cell.updateListView(list);
 615         cell.updateIndex(1);
 616         cell.startEdit();
 617         final boolean[] called = new boolean[] { false };
 618         list.setOnEditCancel(event -> {
 619             called[0] = true;
 620         });
 621         cell.cancelEdit();
 622         assertTrue(called[0]);
 623     }
 624 
 625     @Test public void cancelSetsListViewEditingIndexToNegativeOne() {
 626         list.setEditable(true);
 627         cell.updateListView(list);
 628         cell.updateIndex(1);
 629         cell.startEdit();
 630         cell.cancelEdit();
 631         assertEquals(-1, list.getEditingIndex());
 632         assertFalse(cell.isEditing());
 633     }
 634 
 635     @Ignore
 636     @Test public void movingListCellEditingIndexCausesCurrentlyInEditCellToCancel() {
 637         list.setEditable(true);
 638         cell.updateListView(list);
 639         cell.updateIndex(0);
 640         cell.startEdit();
 641 
 642         ListCell other = new ListCell();
 643         other.updateListView(list);
 644         other.updateIndex(1);
 645         list.edit(1);
 646 
 647         assertTrue(other.isEditing());
 648         assertFalse(cell.isEditing());
 649     }
 650 
 651     // When the list view item's change and affects a cell that is editing, then what?
 652     // When the list cell's index is changed while it is editing, then what?
 653 
 654 
 655     private final class SelectionModelMock extends MultipleSelectionModelBaseShim<String> {
 656         @Override protected int getItemCount() {
 657             return model.size();
 658         }
 659 
 660         @Override protected String getModelItem(int index) {
 661             return model.get(index);
 662         }
 663 
 664         @Override protected void focus(int index) {
 665             // no op
 666         }
 667 
 668         @Override protected int getFocusedIndex() {
 669             return list.getFocusModel().getFocusedIndex();
 670         }
 671     };
 672 
 673     private final class FocusModelMock extends FocusModel {
 674         @Override protected int getItemCount() {
 675             return model.size();
 676         }
 677 
 678         @Override protected Object getModelItem(int row) {
 679             return model.get(row);
 680         }
 681     }
 682 
 683     private int rt_29923_count = 0;
 684     @Test public void test_rt_29923() {
 685         // setup test
 686         cell = new ListCellShim<String>() {
 687             @Override public void updateItem(String item, boolean empty) {
 688                 super.updateItem(item, empty);
 689                 rt_29923_count++;
 690             }
 691         };
 692         list.getItems().setAll(null, null, null);
 693         cell.updateListView(list);
 694 
 695         rt_29923_count = 0;
 696         cell.updateIndex(0);
 697         assertNull(cell.getItem());
 698         assertFalse(cell.isEmpty());
 699         assertEquals(1, rt_29923_count);
 700 
 701         cell.updateIndex(1);
 702         assertNull(cell.getItem());
 703         assertFalse(cell.isEmpty());
 704 
 705         // This test used to be as shown below....but due to RT-33108, it changed
 706         // to the enabled code beneath. Refer to the first comment in RT-33108
 707         // for more detail, but in short we can't optimise and not call updateItem
 708         // when the new and old items are the same - doing so means we can end
 709         // up with bad bindings, etc in the individual cells (in other words,
 710         // even if their item has not changed, the rest of their state may have)
 711 //        assertEquals(1, rt_29923_count);    // even though the index has changed,
 712 //                                            // the item is the same, so we don't
 713 //                                            // update the cell item.
 714         assertEquals(2, rt_29923_count);
 715     }
 716 
 717     @Test public void test_rt_33106() {
 718         cell.updateListView(list);
 719         list.setItems(null);
 720         cell.updateIndex(1);
 721     }
 722 
 723     @Test public void test_jdk_8151524() {
 724         ListCell cell = new ListCell();
 725         cell.setSkin(new ListCellSkin(cell));
 726     }
 727 }