Skip to content

Instantly share code, notes, and snippets.

@dedeibel
Created February 5, 2020 07:52
Show Gist options
  • Save dedeibel/02578e276c601448fab8ef014e326e03 to your computer and use it in GitHub Desktop.
Save dedeibel/02578e276c601448fab8ef014e326e03 to your computer and use it in GitHub Desktop.
Minimal test case for JDK-8150263 : ObservableListWrapper fires change notifications from sort() and sort(Comparator) even if list is not changed
/*
* This is free and unencumbered software released into the public domain.
*
* Anyone is free to copy, modify, publish, use, compile, sell, or
* distribute this software, either in source code form or as a compiled
* binary, for any purpose, commercial or non-commercial, and by any
* means.
*
* In jurisdictions that recognize copyright laws, the author or authors
* of this software dedicate any and all copyright interest in the
* software to the public domain. We make this dedication for the benefit
* of the public at large and to the detriment of our heirs and
* successors. We intend this dedication to be an overt act of
* relinquishment in perpetuity of all present and future rights to this
* software under copyright law.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* For more information, please refer to <https://unlicense.org>
*/
import static org.junit.Assert.fail;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import com.sun.javafx.collections.SortableList;
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.stage.Stage;
/*
* Test case for JDK-8150263 : ObservableListWrapper fires change notifications from sort() and sort(Comparator) even if
* list is not changed.
* https://bugs.java.com/bugdatabase/view_bug.do?bug_id=8150263
*
* Unexpected permutation change event even when sorting the list which is already sorted.
*
* Source seems to be the call to "fireChange" in com.sun.javafx.collections.ObservableListWrapper.sort().
*
* - Method seems unchanged in newer versions
* https://github.com/openjdk/jfx/blob/master/modules/javafx.base/src/main/java/com/sun/javafx/collections/
* ObservableListWrapper.java#L212
* https://github.com/openjdk/jfx/blob/jdk-10%2B15/modules/javafx.base/src/main/java/com/sun/javafx/collections/
* ObservableListWrapper.java#L212
*/
public class FXObservableListWrapperTest {
static CountDownLatch startupLatch;
public static class TestApp extends Application {
@Override
public void start(Stage stage) throws Exception {
startupLatch.countDown();
}
}
private ObservableList<String> sortableList;
public FXObservableListWrapperTest() {
}
@BeforeClass
public static void initJavaFX() {
startupLatch = new CountDownLatch(1);
new Thread(() -> Application.launch(TestApp.class, (String[]) null)).start();
try {
if (!startupLatch.await(15, TimeUnit.SECONDS)) {
fail("Timeout waiting for FX runtime to start");
}
} catch (InterruptedException ex) {
fail("Unexpected exception: " + ex);
}
}
@Before
public void initList() throws Exception {
sortableList = FXCollections.observableArrayList();
}
@Test
public void isSortableList() throws Exception {
// Causing com.sun.javafx.collections.ObservableListWrapper.sort() to be used
Assert.assertTrue(sortableList instanceof SortableList);
}
@Test
public void testExpectedWasPermutatedChange() {
sortableList.addAll("B", "A");
AtomicInteger changesCalledWithoutExceptions = new AtomicInteger();
sortableList.addListener((ListChangeListener<String>) changes -> {
Assert.assertTrue(changes.next());
Assert.assertTrue(changes.wasPermutated());
changesCalledWithoutExceptions.incrementAndGet();
});
// equivalent to FXCollections.sort
FXCollections.sort(sortableList);
Assert.assertEquals(List.of("A", "B"), sortableList);
Assert.assertEquals(1, changesCalledWithoutExceptions.get());
}
@Test
public void testUnexpectedWasPermutatedChange() {
sortableList.addAll("A", "B");
AtomicInteger changesCalled = new AtomicInteger();
sortableList.addListener((ListChangeListener<String>) changes -> {
changesCalled.incrementAndGet();
// Should already be false but keep going for now
// Assert.assertFalse("Unexpected list change", changes.next());
changes.next();
Assert.assertFalse("Unexpected permutation change", changes.wasPermutated());
});
// equivalent to FXCollections.sort
FXCollections.sort(sortableList);
// unreached
Assert.assertEquals(List.of("A", "B"), sortableList);
Assert.assertEquals(0, changesCalled.get());
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment